package com.anjlab.eclipse.tapestry5.actions;
import java.io.InputStream;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.IViewActionDelegate;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import com.anjlab.eclipse.tapestry5.EclipseUtils;
import com.anjlab.eclipse.tapestry5.TapestryContext;
import com.anjlab.eclipse.tapestry5.TapestryFile;
import com.anjlab.eclipse.tapestry5.TapestryModule;
import com.anjlab.eclipse.tapestry5.TapestryProject;
import com.anjlab.eclipse.tapestry5.TapestryUtils;
import com.anjlab.eclipse.tapestry5.templates.ProjectSettings;
import com.anjlab.eclipse.tapestry5.templates.TapestryTemplates;
import com.anjlab.eclipse.tapestry5.views.context.TapestryContextView;
import com.google.common.base.CaseFormat;
public class CreateActionViewDelegate implements IViewActionDelegate, IMenuCreator
{
private static final String SRC_TEST_RESOURCES = "src/test/resources";
private static final String SRC_TEST_JAVA = "src/test/java";
private static final String SRC_MAIN_RESOURCES = "src/main/resources";
private static final String SRC_MAIN_JAVA = "src/main/java";
private IWorkbenchWindow window;
private IViewPart view;
@Override
public void run(IAction action)
{
Menu menu = getMenu(window.getShell());
if (menu.getItemCount() > 0)
{
menu.setVisible(true);
}
else
{
EclipseUtils.openInformation(window,
"Try selecting your Tapestry5 project in the Package Explorer.");
}
}
private IAction newTextFileMenuItem(Menu menu, TapestryContext tapestryContext, String title, String fileName)
{
NewFileWizardAction newFile = new NewFileWizardAction(tapestryContext.getProject(),
tapestryContext,
window.getShell(),
window);
newFile.setFileName(fileName);
String packageName = tapestryContext.getPackageName();
newFile.setFolder(getSourceFolder(tapestryContext, true)
+ "/" + (packageName == null ? "" : packageName.replaceAll("\\.", "/")));
newFile.setText(title);
newFile.setImageDescriptor(PlatformUI.getWorkbench().getEditorRegistry()
.getImageDescriptor(fileName));
return addActionToMenu(menu, newFile);
}
private IAction newJavaClassMenuItem(Menu menu, IProject project, String title, String sourceFolder, String packageName, String typeName)
{
NewJavaClassWizardAction newJavaClass = new NewJavaClassWizardAction(project);
newJavaClass.setText(title);
newJavaClass.setSourceFolder(sourceFolder);
newJavaClass.setPackageName(packageName);
newJavaClass.setTypeName(typeName);
return addActionToMenu(menu, newJavaClass);
}
private IAction newEditProjectSettingsAction(Menu menu, TapestryProject tapestryProject, ProjectSettings settings)
{
OpenFileInTargetFolderAction editSettings =
new EditProjectSettingsAction(window, tapestryProject);
editSettings.setText("Project Settings...");
return addActionToMenu(menu, editSettings);
}
private void newEditDefaultTemplatesAction(Menu menu, TapestryProject project, ProjectSettings settings)
{
MenuItem editCascade = new MenuItem(menu, SWT.CASCADE);
editCascade.setText("Edit Templates");
Menu editMenu = new Menu(menu);
editCascade.setMenu(editMenu);
TapestryTemplates tapestryTemplates = TapestryTemplates.get(project);
IFolder templatesDir = project.getProject()
.getFolder(TapestryUtils.SRC_MAIN_ECLIPSE_TAPESTRY5);
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "page.tml");
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "page.js");
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "page.css");
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "page.properties");
new MenuItem(editMenu, SWT.SEPARATOR);
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "component.tml");
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "component.js");
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "component.css");
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "component.properties");
new MenuItem(editMenu, SWT.SEPARATOR);
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "mixin.js");
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "mixin.css");
addTemplateToMenu(editMenu, tapestryTemplates, templatesDir, "mixin.properties");
}
private void addTemplateToMenu(
final Menu editMenu,
final TapestryTemplates tapestryTemplates,
final IFolder templatesDir,
final String fileName)
{
addActionToMenu(
editMenu,
new OpenFileInTargetFolderAction(
window,
templatesDir,
fileName)
{
@Override
protected InputStream getInitialContents()
{
Path path = new Path(fileName);
return tapestryTemplates.openTemplate(
null,
path.removeFileExtension().lastSegment(),
path.getFileExtension());
}
}).setText(fileName);
}
private IAction addActionToMenu(Menu menu, IAction action)
{
new ActionContributionItem(action).fill(menu, -1);
return action;
}
@Override
public void selectionChanged(IAction action, ISelection selection)
{
}
@Override
public void dispose()
{
}
@Override
public Menu getMenu(Control parent)
{
Menu menu = new Menu(parent);
TapestryContext tapestryContext = view instanceof TapestryContextView
? ((TapestryContextView) view).getTapestryContext()
: TapestryUtils.createTapestryContext(window); // From Main ToolBar
ProjectSettings settings;
TapestryModule module;
if (tapestryContext != null && !tapestryContext.isEmpty())
{
module = TapestryUtils.getTapestryModule(window, tapestryContext.getProject());
settings = resolveProjectSettings(module);
fillMenuForTapestryContext(menu, tapestryContext, settings);
}
else
{
IProject project = EclipseUtils.getProjectFromSelection(
EclipseUtils.getProjectExplorerSelection(window));
module = TapestryUtils.getTapestryModule(window, project);
settings = resolveProjectSettings(module);
}
fillMenuForTapestryModule(menu, module, settings);
return menu;
}
private ProjectSettings resolveProjectSettings(TapestryModule module)
{
return TapestryUtils.readProjectSettings(module != null ? module.getProject() : null);
}
protected void fillMenuForTapestryContext(
Menu menu, TapestryContext tapestryContext, ProjectSettings settings)
{
if (!tapestryContext.isEmpty() && tapestryContext.getName() != null)
{
if (tapestryContext.isReadOnly())
{
newTextFileMenuItem(menu, tapestryContext, "This Context is Read-Only", "")
.setEnabled(false);
return;
}
String packageName = tapestryContext.getPackageName();
newJavaClassMenuItem(menu, tapestryContext.getProject(),
"Create " + tapestryContext.getName() + ".java...",
getSourceFolder(tapestryContext, false),
packageName != null ? packageName : TapestryUtils.getPagesPackage(tapestryContext.getProject()), tapestryContext.getName())
.setEnabled(tapestryContext.getJavaFile() == null);
String[] extensions = new String[] { ".tml", ".properties", ".js", ".css" };
// Use ProjectSettings#fileNamingConvention for suggested names
// (except *.java, *.tml, and *.properties)
for (String extension : extensions)
{
String fileNameWithoutExtension = tapestryContext.getName();
String fileName = fileNameWithoutExtension + extension;
if (StringUtils.equals(extension, ".tml")
|| StringUtils.equals(extension, ".properties"))
{
// Case for these should be the same as context name: UpperCamel
}
else
{
for (Entry<String, String> entry : settings.getFileNamingConventions().entrySet())
{
String regex = TapestryUtils.createRegexpFromGlob(entry.getKey());
if (Pattern.matches(regex, fileName))
{
CaseFormat format = toCaseFormat(entry.getValue());
fileNameWithoutExtension =
CaseFormat.UPPER_CAMEL.to(
format, fileNameWithoutExtension);
break;
}
}
}
fileName = fileNameWithoutExtension + extension;
newTextFileMenuItem(menu, tapestryContext, "Create " + fileName + "...", fileName)
.setEnabled(!tapestryContext.contains(fileName));
}
newTextFileMenuItem(menu, tapestryContext, "Create other...",
CaseFormat.UPPER_CAMEL.to(
toCaseFormat(settings.getFileNamingConventions().get("*.*")),
tapestryContext.getName()));
}
}
private String getSourceFolder(
TapestryContext tapestryContext, boolean forResource)
{
if (forResource)
{
TapestryFile javaFile = tapestryContext.getJavaFile();
if (javaFile != null)
{
if (SRC_TEST_JAVA.equals(
findSourceFolderProjectRelativePath(javaFile)))
{
return SRC_TEST_RESOURCES;
}
}
// No Java file -> use default resource folder
return SRC_MAIN_RESOURCES;
}
TapestryFile templateFile = tapestryContext.getTemplateFile();
if (templateFile != null)
{
if (SRC_TEST_RESOURCES.equals(
findSourceFolderProjectRelativePath(templateFile)))
{
return SRC_TEST_JAVA;
}
}
// No template file -> use default java folder
return SRC_MAIN_JAVA;
}
private String findSourceFolderProjectRelativePath(TapestryFile tapestryFile)
{
try
{
IResource resource =
tapestryFile
.getProject()
.findMember(tapestryFile.getPath());
if (resource == null)
{
return null;
}
IContainer container = resource.getParent();
while (container != null && !EclipseUtils.isSourceFolder(container))
{
container = container.getParent();
}
return container != null
? container.getProjectRelativePath().toPortableString()
: null;
}
catch (JavaModelException e)
{
// Ignore
return null;
}
}
private static CaseFormat toCaseFormat(String convention)
{
if (StringUtils.equalsIgnoreCase(convention, "UpperCamel"))
{
return CaseFormat.UPPER_CAMEL;
}
if (StringUtils.equalsIgnoreCase(convention, "lowerCamel"))
{
return CaseFormat.LOWER_CAMEL;
}
if (StringUtils.equalsIgnoreCase(convention, "lower_underscode"))
{
return CaseFormat.LOWER_UNDERSCORE;
}
if (StringUtils.equalsIgnoreCase(convention, "lower-hyphen"))
{
return CaseFormat.LOWER_HYPHEN;
}
return CaseFormat.UPPER_CAMEL;
}
protected void fillMenuForTapestryModule(Menu menu, TapestryModule module, ProjectSettings settings)
{
IProject project = module != null ? module.getEclipseProject() : null;
if (module == null
|| module.isReadOnly()
// TODO Support creating classes for any non-ReadOnly TapestryModules,
// not only for the AppModule
|| !TapestryUtils.isTapestryProject(project))
{
return;
}
if (menu.getItemCount() > 0)
{
new MenuItem(menu, SWT.SEPARATOR);
}
newJavaClassMenuItem(menu, project,
"New Page Class...",
SRC_MAIN_JAVA,
// TODO Get package for TapestryModule
// Check if TapestryModule.isAppModule() then resolve package via IProject,
// otherwise via LibraryMappings
// TODO Support LibraryMappings for workspace projects -- they usually don't provide MANIFEST.MF
// maybe try searching manifests in build folders of the projects?
TapestryUtils.getPagesPackage(project), null);
newJavaClassMenuItem(menu, project,
"New Component Class...",
SRC_MAIN_JAVA,
// TODO Get package for TapestryModule
TapestryUtils.getComponentsPackage(project), null);
newJavaClassMenuItem(menu, project,
"New Mixin Class...",
SRC_MAIN_JAVA,
// TODO Get package for TapestryModule
TapestryUtils.getMixinsPackage(project), null);
new MenuItem(menu, SWT.SEPARATOR);
newEditDefaultTemplatesAction(menu, module.getProject(), settings);
newEditProjectSettingsAction(menu, module.getProject(), settings);
}
@Override
public Menu getMenu(Menu parent)
{
return null;
}
@Override
public void init(IViewPart view)
{
this.view = view;
this.window = view.getSite().getWorkbenchWindow();
}
public void init(IWorkbenchWindow window)
{
this.window = window;
}
}