/*
* Playclipse - Eclipse plugin for the Play! Framework
* Copyright 2009 Zenexity
*
* This file is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.playframework.playclipse.handlers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageDeclaration;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.handlers.HandlerUtil;
import org.playframework.playclipse.FilesAccess;
import org.playframework.playclipse.Navigation;
import org.playframework.playclipse.PlayPlugin;
import cn.bran.japid.util.DirUtil;
import fr.zenexity.pdt.editors.EditorHelper;
/**
* Go to the view (template) corresponding to the current action
*/
public class GoToViewHandler extends AbstractHandler {
static Pattern stringPattern = Pattern.compile("\"(.*)\"");
static Pattern methodNamePattern = Pattern.compile("\\w+\\s*\\(");
static Pattern renderJapidPattern = Pattern.compile("renderJapid\\s*\\(");
static Pattern renderJapidWithPattern = Pattern.compile("renderJapidWith\\s*\\(\\s*\"(.*)\"");
private IWorkbenchWindow window;
/**
* The constructor.
*/
public GoToViewHandler() {
}
// @SuppressWarnings("restriction")
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
EditorHelper editor = EditorHelper.getCurrent(event);
IProject project = editor.getProject();
IJavaProject jProject = JavaCore.create(project);
if(editor != null) {
String relativePath = ((IFileEditorInput) editor.textEditor.getEditorInput()).getFile().getProjectRelativePath().toString();
if (relativePath.startsWith("app/japidviews/") && relativePath.endsWith(".java")) {
// bran: if we are in the japid derived Java template code, let's switch back to the html view
String jFile = DirUtil.mapJavaToSrc(relativePath);
IFile f = project.getFile(jFile);
try {
FilesAccess.openFile(f);
} catch (CoreException e) {
PlayPlugin.showError(e);
}
return null;
}
}
boolean useJapid = true;
String line;
String viewName = null;
String title = editor.getTitle();
String controllerName = title.replace(".java", "");
String packageName = "";
IEditorInput editorInput = editor.textEditor.getEditorInput();
ITextSelection selection = (ITextSelection) editor.textEditor.getSelectionProvider().getSelection();
IJavaElement elem = JavaUI.getEditorInputJavaElement(editorInput);
ICompilationUnit unit = null;
if (elem instanceof ICompilationUnit) {
unit = (ICompilationUnit) elem;
try {
IPackageDeclaration[] packs = unit.getPackageDeclarations();
if (packs.length < 1) {
info("This action can only apply to controllers.");
return null;
} else {
packageName = packs[0].getElementName();
if (!packageName.startsWith("controllers")) {
info("This action can only apply to controllers.");
return null;
}
}
// get the class declaration line
IType type = unit.getType(controllerName);
ITypeHierarchy superTypes = type.newSupertypeHierarchy(null);
// String name = JapidController.class.getName(); // this will require play.jar
String name = "cn.bran.play.JapidController";
IType japidController = jProject.findType(name);
if (superTypes.contains(japidController)) {
useJapid = true;
}
else {
useJapid = false;
}
// String superclassName = type.getSuperclassName();
// if (superclassName.toLowerCase().contains("japid")) {
// useJapid = true;
// }
// current selected elem
IJavaElement[] elements= unit.codeSelect(selection.getOffset(), selection.getLength());
if (elements.length > 0) {
// TODO extract the current selection to tell if the cursor in on renderJapidXXX line
System.out.println(elements);
}
} catch (JavaModelException e) {
e.printStackTrace();
}
} else {
System.out.println(elem.getElementType() + ":" + elem.getElementName());
}
viewName = getEnclosingActionName(selection, unit);
int lineNo = editor.getCurrentLineNo();
line = editor.getLine(lineNo);
if (line.contains("render")) {
if (!line.contains("renderJapid")) {
Pattern pt = Pattern.compile("\"(.*)\"");
Matcher m = pt.matcher(line);
if (m.find()) {
// render classic groovy
useJapid = false;
// There is a custom view, by renderTemplate()
viewName = "app/views/" + m.group().replace("\"", "");
}
} else {
// render japid
useJapid = true;
if(line.contains("renderJapidWith")) {
// explicit template name
Matcher m = stringPattern.matcher(line);
if (m.find()) {
// There is a custom view
// the template strin is the view name in relative to the japidviews directory
viewName = "app/japidviews/" + m.group().replace("\"", "");
}
else {
System.out.println("the first param of renderJapidWith is not a String. Strange....");
}
}
}
}
if (viewName == null) {
String string = "Use this command in a controller action body, or on a render...() line";
info(string);
} else {
if (!viewName.startsWith("app")) {
viewName = "app/" + (useJapid? "japidviews" : "views") + "/"
+ (packageName.equals("controllers") ? "" : packageName.substring(12).replace('.', '/') + "/")
+ controllerName + "/" + viewName + ".html";
}
(new Navigation(editor)).goToViewAbs(viewName);
}
return null;
}
/**
* @param selection
* @param unit
*/
private String getEnclosingActionName(ITextSelection selection, ICompilationUnit unit) {
IJavaElement selected;
try {
selected = unit.getElementAt(selection.getOffset());
List<IJavaElement> path = getJavaElementsPath(selected);
if (path.size() >= 7) {
IJavaElement el = path.get(6);
if (el.getElementType() == IJavaElement.METHOD) {
IMethod sm = (IMethod)el;
int flags = sm.getFlags();
String actionMethodName = el.getElementName();
if (/*Flags.isPublic(flags) &&*/ Flags.isStatic(flags)) {
return actionMethodName;
}
else {
info("The enclosig method " + actionMethodName + " is not public static, thus not a valid action method.");
}
}
}
}
catch (JavaModelException e) {
PlayPlugin.showError(e);
}
return null;
}
static void printAstPath(IJavaElement elem) {
System.out.println(elem.getClass() + ":" + elem.getElementType() + ":" + elem.getElementName());
IJavaElement parent = elem.getParent();
if (parent != null) {
printAstPath(parent);
}
}
/**
* the result pattern:
* {JavaModel, JavaProject, packageFragmentRoot, PackageFragment (package name),
* CompilationUnit (source file title), SouceType (class name}...
* @param elem
* @return
*/
public static List<IJavaElement> getJavaElementsPath(IJavaElement elem) {
List<IJavaElement> path = new ArrayList<IJavaElement>();
if (elem == null)
return path;
path.add(elem);
elem = elem.getParent();
while (elem != null) {
path.add(elem);
elem = elem.getParent();
}
Collections.reverse(path);
return path;
}
/**
* @param window
* @param string
*/
private void info(String string) {
MessageDialog.openInformation(window.getShell(), "Playclipse", string);
}
}