Java tutorial
/******************************************************************************* * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * *******************************************************************************/ package com.liferay.ide.service.ui.editor; import com.liferay.ide.core.util.CoreUtil; import com.liferay.ide.service.core.util.ServiceUtil; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.ICodeAssist; 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.ITypeHierarchy; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jdt.internal.corext.util.JdtFlags; import org.eclipse.jdt.internal.ui.actions.SelectionConverter; import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; import org.eclipse.jdt.internal.ui.text.JavaWordFinder; import org.eclipse.jdt.ui.actions.SelectionDispatchAction; import org.eclipse.jface.action.IAction; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; /** * @author Gregory Amerson * */ @SuppressWarnings("restriction") public class ServiceMethodHyperlinkDetector extends AbstractHyperlinkDetector { private static class IMethodWrapper { private final boolean base; private final IMethod method; public IMethodWrapper(IMethod method, boolean base) { this.method = method; this.base = base; } } private static class WrapperMethodCollector extends SearchRequestor { private final List<IMethod> results; private final IMethod method; public WrapperMethodCollector(List<IMethod> results, IMethod method) { super(); this.results = results; this.method = method; } @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { final Object element = match.getElement(); if (element instanceof IMethod && matches((IMethod) element)) { this.results.add((IMethod) element); } } private boolean matches(IMethod element) throws JavaModelException { boolean matches = false; if (this.method.getNumberOfParameters() == element.getNumberOfParameters()) { matches = true; for (int i = 0; i < this.method.getTypeParameters().length; i++) { if (!this.method.getParameterTypes()[i].equals(element.getParameterTypes()[i])) { matches = false; break; } } } return matches; } } private IJavaElement[] lastElements; private ITypeRoot lastInput; private long lastModStamp; private IRegion lastWordRegion; private void addHyperlinks(final List<IHyperlink> links, final IRegion word, final SelectionDispatchAction openAction, final IMethod method, final boolean qualify, final JavaEditor editor) { if (shouldAddServiceHyperlink(editor)) { final IMethod implMethod = getServiceImplMethod(method); if (implMethod != null) { links.add(new ServiceMethodImplementationHyperlink(word, openAction, implMethod, qualify)); } final IMethodWrapper wrapperMethod = getServiceWrapperMethod(method); if (wrapperMethod != null) { if (wrapperMethod.base) { links.add(new ServiceMethodWrapperLookupHyperlink(editor, word, openAction, wrapperMethod.method, qualify)); } else { links.add(new ServiceMethodWrapperHyperlink(word, openAction, wrapperMethod.method, qualify)); } } } } public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { IHyperlink[] retval = null; final ITextEditor textEditor = (ITextEditor) getAdapter(ITextEditor.class); if (textEditor == null) { return retval; } final ITypeRoot input = EditorUtility.getEditorInputJavaElement(textEditor, false); final IAction openAction = textEditor.getAction("OpenEditor"); if (shouldDetectHyperlinks(textEditor, input, openAction, region)) { final IDocumentProvider documentProvider = textEditor.getDocumentProvider(); final IEditorInput editorInput = textEditor.getEditorInput(); final IDocument document = documentProvider.getDocument(editorInput); final int offset = region.getOffset(); final IRegion wordRegion = JavaWordFinder.findWord(document, offset); if (isRegionValid(document, wordRegion)) { IJavaElement[] elements = new IJavaElement[0]; final long modStamp = documentProvider.getModificationStamp(editorInput); if (input.equals(this.lastInput) && modStamp == this.lastModStamp && wordRegion.equals(this.lastWordRegion)) { elements = this.lastElements; } else { try { elements = ((ICodeAssist) input).codeSelect(wordRegion.getOffset(), wordRegion.getLength()); elements = selectOpenableElements(elements); this.lastInput = input; this.lastModStamp = modStamp; this.lastWordRegion = wordRegion; this.lastElements = elements; } catch (JavaModelException e) { } } if (elements.length != 0) { final List<IHyperlink> links = new ArrayList<IHyperlink>(elements.length); for (IJavaElement element : elements) { if (element instanceof IMethod) { addHyperlinks(links, wordRegion, (SelectionDispatchAction) openAction, (IMethod) element, elements.length > 1, (JavaEditor) textEditor); } } if (links.size() != 0) { if (canShowMultipleHyperlinks) { retval = links.toArray(new IHyperlink[0]); } else { retval = new IHyperlink[] { links.get(0) }; } } } } } return retval; } @Override public void dispose() { super.dispose(); this.lastElements = null; this.lastInput = null; this.lastWordRegion = null; } private IType findType(IJavaElement parent, String fullyQualifiedName) throws JavaModelException { IType retval = parent.getJavaProject().findType(fullyQualifiedName); if (retval == null) { final IJavaProject[] serviceProjects = ServiceUtil.getAllServiceProjects(); for (final IJavaProject sp : serviceProjects) { try { retval = sp.findType(fullyQualifiedName); } catch (Exception e) { } if (retval != null) { break; } } } return retval; } private IMethod getServiceImplMethod(final IMethod method) { IMethod retval = null; try { final IJavaElement methodClass = method.getParent(); final IType methodClassType = method.getDeclaringType(); final String methodClassName = methodClass.getElementName(); if (methodClassName.endsWith("Util") && JdtFlags.isPublic(method) && JdtFlags.isStatic(method)) { final String packageName = methodClassType.getPackageFragment().getElementName(); final String baseServiceName = methodClassName.substring(0, methodClassName.length() - 4); // as per liferay standard real implementation will be in impl package and Impl suffix // e.g. com.example.service.FooUtil.getBar() --> com.example.service.impl.FooImpl.getBar() final String fullyQualifiedName = packageName + ".impl." + baseServiceName + "Impl"; final IType implType = findType(methodClass, fullyQualifiedName); if (implType != null) { IMethod[] methods = implType.findMethods(method); if (CoreUtil.isNullOrEmpty(methods)) { final ITypeHierarchy hierarchy = implType.newSupertypeHierarchy(new NullProgressMonitor()); IType currentType = implType; while (retval == null && currentType != null) { methods = currentType.findMethods(method);// match name and arguments if (!CoreUtil.isNullOrEmpty(methods)) { retval = methods[0]; } else { currentType = hierarchy.getSuperclass(currentType); } } } else { retval = methods[0]; } } } } catch (Exception e) { } return retval; } private IMethodWrapper getServiceWrapperMethod(final IMethod method) { IMethodWrapper retval = null; try { final IJavaElement methodClass = method.getParent(); final IType methodClassType = method.getDeclaringType(); final String methodClassName = methodClass.getElementName(); if (methodClassName.endsWith("Util") && JdtFlags.isPublic(method) && JdtFlags.isStatic(method)) { final String packageName = methodClassType.getPackageFragment().getElementName(); final String baseServiceName = methodClassName.substring(0, methodClassName.length() - 4); // as per liferay standard wrapper type will be in service package with Wrapper suffix // e.g. com.example.service.FooUtil.getBar() --> com.example.service.FooWrapper.getBar() final String fullyQualifiedName = packageName + "." + baseServiceName + "Wrapper"; final IType wrapperType = findType(methodClass, fullyQualifiedName); if (wrapperType != null) { IMethod[] wrapperBaseMethods = wrapperType.findMethods(method); if (!CoreUtil.isNullOrEmpty(wrapperBaseMethods)) { // look for classes that implement this wrapper final List<IMethod> overrides = new ArrayList<IMethod>(); final SearchRequestor requestor = new WrapperMethodCollector(overrides, method); final IJavaSearchScope scope = SearchEngine.createStrictHierarchyScope(null, wrapperType, true, false, null); final SearchPattern search = SearchPattern.createPattern(method.getElementName(), IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); new SearchEngine().search(search, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope, requestor, new NullProgressMonitor()); if (overrides.size() > 1) { retval = new IMethodWrapper(wrapperBaseMethods[0], true); } else if (overrides.size() == 1) { retval = new IMethodWrapper(overrides.get(0), false); } } } } } catch (Exception e) { } return retval; } private boolean isInheritDoc(IDocument document, IRegion wordRegion) { try { String word = document.get(wordRegion.getOffset(), wordRegion.getLength()); return "inheritDoc".equals(word); } catch (BadLocationException e) { return false; } } private boolean isRegionValid(IDocument document, IRegion wordRegion) { if (wordRegion != null && wordRegion.getLength() != 0 && (!isInheritDoc(document, wordRegion))) { return true; } return false; } private IJavaElement[] selectOpenableElements(IJavaElement[] elements) { final List<IJavaElement> result = new ArrayList<IJavaElement>(elements.length); for (int i = 0; i < elements.length; i++) { final IJavaElement element = elements[i]; switch (element.getElementType()) { case IJavaElement.PACKAGE_DECLARATION: case IJavaElement.PACKAGE_FRAGMENT: case IJavaElement.PACKAGE_FRAGMENT_ROOT: case IJavaElement.JAVA_PROJECT: case IJavaElement.JAVA_MODEL: break; default: result.add(element); break; } } return result.toArray(new IJavaElement[result.size()]); } private boolean shouldAddServiceHyperlink(final JavaEditor editor) { return SelectionConverter.canOperateOn(editor); } private boolean shouldDetectHyperlinks(final ITextEditor textEditor, final ITypeRoot input, final IAction openAction, final IRegion region) { return region != null && textEditor instanceof JavaEditor && openAction instanceof SelectionDispatchAction && input != null; } }