/*******************************************************************************
* Copyright (c) 2009 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.seam.ui.refactoring;
import java.io.IOException;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IAnnotation;
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.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.ui.text.FastJavaPartitionScanner;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.internal.services.IWorkbenchLocationService;
import org.eclipse.ui.menus.AbstractContributionFactory;
import org.eclipse.ui.menus.IContributionRoot;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.jboss.tools.common.el.core.model.ELInstance;
import org.jboss.tools.common.el.core.model.ELInvocationExpression;
import org.jboss.tools.common.el.core.model.ELModel;
import org.jboss.tools.common.el.core.model.ELPropertyInvocation;
import org.jboss.tools.common.el.core.parser.ELParser;
import org.jboss.tools.common.el.core.parser.ELParserUtil;
import org.jboss.tools.common.model.ui.editor.EditorPartWrapper;
import org.jboss.tools.common.model.util.EclipseJavaUtil;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.jboss.tools.common.propertieseditor.PropertiesCompoundEditor;
import org.jboss.tools.common.util.FileUtil;
import org.jboss.tools.jst.web.ui.editors.WebCompoundEditor;
import org.jboss.tools.seam.core.ISeamComponent;
import org.jboss.tools.seam.core.ISeamJavaComponentDeclaration;
import org.jboss.tools.seam.core.ISeamProject;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.core.SeamCoreMessages;
import org.jboss.tools.seam.internal.core.refactoring.RenameComponentProcessor;
import org.jboss.tools.seam.internal.core.refactoring.RenameComponentRefactoring;
import org.jboss.tools.seam.internal.core.refactoring.RenameSeamContextVariableProcessor;
import org.jboss.tools.seam.ui.SeamGuiPlugin;
import org.jboss.tools.seam.ui.wizard.RenameComponentWizard;
import org.jboss.tools.seam.ui.wizard.RenameSeamContextVariableWizard;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author Daniel Azarov
*/
public class SeamRefactorContributionFactory extends AbstractContributionFactory {
private static final String ANNOTATION_NAME = "org.jboss.seam.annotations.Name"; //$NON-NLS-1$
private static final String JAVA_EXT = "java"; //$NON-NLS-1$
private static final String XML_EXT = "xml"; //$NON-NLS-1$
private static final String XHTML_EXT = "xhtml"; //$NON-NLS-1$
private static final String JSP_EXT = "jsp"; //$NON-NLS-1$
private static final String PROPERTIES_EXT = "properties"; //$NON-NLS-1$
private static final String GET = "get"; //$NON-NLS-1$
private static final String SET = "set"; //$NON-NLS-1$
private static final String IS = "is"; //$NON-NLS-1$
static private String selectedText;
static private IFile editorFile;
private String fileContent;
private IEditorPart editor;
private Shell shell;
public SeamRefactorContributionFactory(){
super("","");
}
public SeamRefactorContributionFactory(String location, String namespace){
super(location, namespace);
}
@Override
public void createContributionItems(IServiceLocator serviceLocator,
IContributionRoot additions) {
if(serviceLocator.hasService(IWorkbenchLocationService.class)){
IWorkbenchLocationService service = (IWorkbenchLocationService)serviceLocator.getService(IWorkbenchLocationService.class);
editor = service.getWorkbenchWindow().getActivePage().getActiveEditor();
shell = service.getWorkbench().getActiveWorkbenchWindow().getShell();
if(!(editor.getEditorInput() instanceof FileEditorInput))
return;
FileEditorInput input = (FileEditorInput)editor.getEditorInput();
editorFile = input.getFile();
String ext = editorFile.getFileExtension();
if (!JAVA_EXT.equalsIgnoreCase(ext)
&& !XML_EXT.equalsIgnoreCase(ext)
&& !XHTML_EXT.equalsIgnoreCase(ext)
&& !JSP_EXT.equalsIgnoreCase(ext)
&& !PROPERTIES_EXT.equalsIgnoreCase(ext))
return;
ISeamProject seamProject = SeamCorePlugin.getSeamProject(editorFile.getProject(), true);
if(seamProject == null)
return;
MenuManager mm = new MenuManager(SeamCoreMessages.SEAM_REFACTOR);
mm.setVisible(true);
boolean separatorIsAdded = false;
if(JAVA_EXT.equalsIgnoreCase(ext)){
ISeamComponent component = getComponent(editorFile);
if(component != null){
mm.add(new RenameSeamComponentAction());
additions.addContributionItem(new Separator(), null);
additions.addContributionItem(mm, null);
separatorIsAdded = true;
}
}
ISelection sel = editor.getEditorSite().getSelectionProvider().getSelection();
if(sel == null || sel.isEmpty())
return;
if(sel instanceof StructuredSelection){
if(editor instanceof PropertiesCompoundEditor){
sel = ((PropertiesCompoundEditor)editor).getActiveEditor().getSite().getSelectionProvider().getSelection();
}else if(editor instanceof EditorPartWrapper){
EditorPartWrapper wrapperEditor = (EditorPartWrapper)editor;
if(wrapperEditor.getEditor() instanceof WebCompoundEditor){
WebCompoundEditor xmlEditor = (WebCompoundEditor)wrapperEditor.getEditor();
sel = xmlEditor.getActiveEditor().getSite().getSelectionProvider().getSelection();
}
}else if(editor instanceof WebCompoundEditor)
sel = ((WebCompoundEditor)editor).getActiveEditor().getSite().getSelectionProvider().getSelection();
}
if(sel instanceof TextSelection){
TextSelection selection = (TextSelection)sel;
selectedText = selection.getText();
try {
fileContent = FileUtil.readStream(editorFile);
} catch (CoreException e) {
SeamGuiPlugin.getDefault().logError(e);
}
boolean status = false;
if(JAVA_EXT.equalsIgnoreCase(ext)){
// check - whether selected component's name or not
// if(checkNameAnnotation(selection)){
// mm.add(new RenameSeamComponentAction());
//
// additions.addContributionItem(mm, null);
// }
//checkPropertyName(selection, mm, additions);
status = checkContextVariableInJava(editorFile, fileContent, selection);
} else if(XML_EXT.equalsIgnoreCase(ext) || XHTML_EXT.equalsIgnoreCase(ext) || JSP_EXT.equalsIgnoreCase(ext))
status = checkContextVariableInDOM(editorFile, fileContent, selection);
else if(PROPERTIES_EXT.equalsIgnoreCase(ext))
status = checkContextVariableInProperties(editorFile, fileContent, selection);
if(status){
mm.add(new RenameSeamContextVariableAction());
if(!separatorIsAdded)
additions.addContributionItem(new Separator(), null);
additions.addContributionItem(mm, null);
}
}
}
}
private boolean checkContextVariableInJava(IFile file, String content, TextSelection selection){
try {
FastJavaPartitionScanner scaner = new FastJavaPartitionScanner();
Document document = new Document(content);
scaner.setRange(document, 0, document.getLength());
IToken token = scaner.nextToken();
while(token!=null && token!=Token.EOF) {
if(IJavaPartitions.JAVA_STRING.equals(token.getData())) {
int length = scaner.getTokenLength();
int offset = scaner.getTokenOffset();
if(offset <= selection.getOffset() && (offset+length) >= (selection.getOffset()+selection.getLength())){
String value = document.get(offset, length);
if(value.indexOf('{')>-1) {
return scanString(file, value, offset, selection);
}
}
}
token = scaner.nextToken();
}
} catch (BadLocationException e) {
SeamCorePlugin.getDefault().logError(e);
}
return false;
}
private boolean scanString(IFile file, String string, int offset, TextSelection selection) {
int startEl = string.indexOf("#{"); //$NON-NLS-1$
if(startEl>-1) {
ELParser parser = ELParserUtil.getJbossFactory().createParser();
ELModel model = parser.parse(string);
for (ELInstance instance : model.getInstances()) {
for(ELInvocationExpression ie : instance.getExpression().getInvocations()){
ELPropertyInvocation pi = findSeamContextVariable(ie);
if(pi != null){
if(offset+pi.getStartPosition() == selection.getOffset() && pi.getLength() == selection.getLength())
return true;
}
}
}
}
return false;
}
private ELPropertyInvocation findSeamContextVariable(ELInvocationExpression invocationExpression){
ELInvocationExpression invExp = invocationExpression;
while(invExp != null){
if(invExp instanceof ELPropertyInvocation){
if(((ELPropertyInvocation)invExp).getQualifiedName() != null && ((ELPropertyInvocation)invExp).getQualifiedName().equals(selectedText))
return (ELPropertyInvocation)invExp;
else
invExp = invExp.getLeft();
}else{
invExp = invExp.getLeft();
}
}
return null;
}
private boolean checkContextVariableInDOM(IFile file, String content, TextSelection selection){
IModelManager manager = StructuredModelManager.getModelManager();
if(manager == null) {
return false;
}
IStructuredModel model = null;
try {
model = manager.getModelForRead(file);
if (model instanceof IDOMModel) {
IDOMModel domModel = (IDOMModel) model;
IDOMDocument document = domModel.getDocument();
return scanChildNodes(file, document, selection);
}
} catch (CoreException e) {
SeamCorePlugin.getDefault().logError(e);
} catch (IOException e) {
SeamCorePlugin.getDefault().logError(e);
} finally {
if (model != null) {
model.releaseFromRead();
}
}
return false;
}
private boolean scanChildNodes(IFile file, Node parent, TextSelection selection) {
boolean status = false;
if(parent == null)
return false;
NodeList children = parent.getChildNodes();
for(int i=0; i<children.getLength(); i++) {
Node curentValidatedNode = children.item(i);
if(Node.ELEMENT_NODE == curentValidatedNode.getNodeType()) {
status = scanNodeContent(file, ((IDOMNode)curentValidatedNode).getFirstStructuredDocumentRegion(), DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE, selection);
if(status)
return status;
} else if(Node.TEXT_NODE == curentValidatedNode.getNodeType()) {
status = scanNodeContent(file, ((IDOMNode)curentValidatedNode).getFirstStructuredDocumentRegion(), DOMRegionContext.XML_CONTENT, selection);
if(status)
return status;
}
status = scanChildNodes(file, curentValidatedNode, selection);
if(status)
return status;
}
return false;
}
private boolean scanNodeContent(IFile file, IStructuredDocumentRegion node, String regionType, TextSelection selection) {
boolean status = false;
if(node == null)
return false;
ITextRegionList regions = node.getRegions();
for(int i=0; i<regions.size(); i++) {
ITextRegion region = regions.get(i);
if(region.getType() == regionType) {
String text = node.getFullText(region);
if(text.indexOf("{")>-1) { //$NON-NLS-1$
int offset = node.getStartOffset() + region.getStart();
status = scanString(file, text, offset, selection);
if(status)
return status;
}
}
}
return false;
}
private boolean checkContextVariableInProperties(IFile file, String content, TextSelection selection){
return scanString(file, content, 0, selection);
}
private ISeamComponent getComponent(IFile file){
IProject project = file.getProject();
ISeamProject seamProject = SeamCorePlugin.getSeamProject(project, true);
if (seamProject != null) {
Set<ISeamComponent> components = seamProject.getComponentsByPath(editorFile.getFullPath());
for(ISeamComponent component : components){
ISeamJavaComponentDeclaration declaration = component.getJavaDeclaration();
if(declaration != null){
IResource resource = declaration.getResource();
if(resource != null && resource.getFullPath().equals(editorFile.getFullPath())){
if(declaration.getName().equals(component.getName())){
return component;
}
}
}
}
}
return null;
}
private IAnnotation getNameAnnotation(IFile file){
try{
ICompilationUnit unit = getCompilationUnit(file);
for(IType type : unit.getAllTypes()){
for(IAnnotation annotation : type.getAnnotations()){
if(EclipseJavaUtil.resolveType(type, annotation.getElementName()).equals(ANNOTATION_NAME))
return annotation;
}
}
}catch(CoreException ex){
SeamCorePlugin.getDefault().logError(ex);
}
return null;
}
private ICompilationUnit getCompilationUnit(IFile file) throws CoreException {
IProject project = file.getProject();
IJavaProject javaProject = (IJavaProject)project.getNature(JavaCore.NATURE_ID);
for (IResource resource : EclipseResourceUtil.getJavaSourceRoots(project)) {
if(resource.getFullPath().isPrefixOf(file.getFullPath())) {
IPath path = file.getFullPath().removeFirstSegments(resource.getFullPath().segmentCount());
IJavaElement element = javaProject.findElement(path);
if(element instanceof ICompilationUnit) {
return (ICompilationUnit)element;
}
}
}
return null;
}
private String getPropertyName(IMethod method){
String name = method.getElementName();
if(name.startsWith(GET) || name.startsWith(SET))
return name.substring(3).toLowerCase();
if(name.startsWith(IS))
return name.substring(2).toLowerCase();
return name.toLowerCase();
}
private static void saveAndBuild(){
if(!SeamGuiPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().saveAllEditors(true))
return;
try {
Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null);
} catch (InterruptedException e) {
// do nothing
}
}
public static void invokeRenameComponentWizard(ISeamComponent component, Shell activeShell) {
saveAndBuild();
RenameComponentProcessor processor = new RenameComponentProcessor(component);
RenameComponentRefactoring refactoring = new RenameComponentRefactoring(processor);
RenameComponentWizard wizard = new RenameComponentWizard(refactoring, component);
RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
try {
String titleForFailedChecks = SeamCoreMessages.SEAM_COMPONENT_RENAME_HANDLER_ERROR;
op.run(activeShell, titleForFailedChecks);
} catch (final InterruptedException irex) {
// operation was canceled
}
//SeamUIUtil.waiteForBuild();
//SeamUIUtil.refreshSeamComponentView();
}
public static void invokeRenameSeamContextVariableWizard(String oldName, Shell activeShell) {
saveAndBuild();
RenameSeamContextVariableProcessor processor = new RenameSeamContextVariableProcessor(editorFile, selectedText);
RenameComponentRefactoring refactoring = new RenameComponentRefactoring(processor);
RenameSeamContextVariableWizard wizard = new RenameSeamContextVariableWizard(refactoring, editorFile);
RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
try {
String titleForFailedChecks = SeamCoreMessages.SEAM_COMPONENT_RENAME_HANDLER_ERROR;
op.run(activeShell, titleForFailedChecks);
} catch (final InterruptedException irex) {
// operation was canceled
}
//SeamUIUtil.waiteForBuild();
//SeamUIUtil.refreshSeamComponentView();
}
class RenameSeamComponentAction extends Action{
public RenameSeamComponentAction(){
super(SeamCoreMessages.RENAME_SEAM_COMPONENT);
}
public void run(){
saveAndBuild();
ISeamComponent component = getComponent(editorFile);
invokeRenameComponentWizard(component, shell);
}
}
class RenameSeamContextVariableAction extends Action{
public RenameSeamContextVariableAction(){
super(SeamCoreMessages.RENAME_SEAM_CONTEXT_VARIABLE);
}
public void run(){
saveAndBuild();
invokeRenameSeamContextVariableWizard(selectedText, shell);
}
}
}