001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.javadoc.search;
043:
044: import com.sun.source.tree.ClassTree;
045: import com.sun.source.tree.CompilationUnitTree;
046: import com.sun.source.tree.Tree;
047: import com.sun.source.util.Trees;
048: import java.io.IOException;
049: import java.net.URL;
050: import java.util.StringTokenizer;
051: import java.util.ArrayList;
052: import java.util.Set;
053: import java.util.Iterator;
054: import java.util.List;
055: import java.util.logging.Level;
056: import java.util.logging.Logger;
057: import javax.lang.model.element.Element;
058: import javax.lang.model.element.ElementKind;
059: import javax.lang.model.element.ExecutableElement;
060: import javax.lang.model.element.TypeElement;
061: import javax.lang.model.element.VariableElement;
062: import javax.lang.model.type.DeclaredType;
063: import javax.lang.model.type.TypeKind;
064: import javax.lang.model.type.TypeMirror;
065: import org.netbeans.api.java.classpath.GlobalPathRegistry;
066: import org.netbeans.api.java.classpath.ClassPath;
067: import org.netbeans.api.java.queries.JavadocForBinaryQuery;
068: import org.netbeans.api.java.queries.SourceForBinaryQuery;
069: import org.netbeans.api.java.source.CancellableTask;
070: import org.netbeans.api.java.source.CompilationController;
071: import org.netbeans.api.java.source.ElementHandle;
072: import org.netbeans.api.java.source.JavaSource;
073: import org.netbeans.api.java.source.JavaSource.Phase;
074: import org.netbeans.api.java.source.TreeUtilities;
075: import org.openide.filesystems.FileObject;
076: import org.openide.filesystems.URLMapper;
077:
078: /** This class finds the source to show it instead of documentation.
079: *
080: */
081: public final class SrcFinder extends Object {
082:
083: /** SrcFinder is a singleton */
084: private SrcFinder() {
085: }
086:
087: static Object[]/*FileObject, ElementHandle*/findSource(
088: String aPackage, URL url) {
089:
090: aPackage = aPackage.replace('.', '/'); // NOI18N
091: String thePackage = null;
092: final String member = url.getRef();
093: String clazz = url.getFile();
094: String filename = null;
095:
096: int pIndex;
097:
098: if ((pIndex = clazz.toLowerCase().lastIndexOf(
099: aPackage.trim().toLowerCase())) != -1) {
100: thePackage = clazz.substring(pIndex, pIndex
101: + aPackage.trim().length() - 1);
102: clazz = clazz.substring(pIndex + aPackage.trim().length(),
103: clazz.length() - 5);
104:
105: int ei;
106: if ((ei = clazz.indexOf('.')) != -1) {
107: filename = clazz.substring(0, ei);
108: } else
109: filename = clazz;
110:
111: }
112:
113: // System.out.println("================================");
114: // System.out.println("URL :" + url );
115: // System.out.println("aPCKG :" + aPackage );
116: // System.out.println("--------------------------------");
117: // System.out.println("MEMBER :" + member ); // NOI18N
118: // System.out.println("CLASS :" + clazz ); // NOI18N
119: // System.out.println("PACKAGE :" + thePackage ); // NOI18N
120: // System.out.println("FILENAME:" + filename ); // NOI18N
121:
122: String resourceName = thePackage + "/" + filename + ".java"; // NOI18N
123: FileObject fo = searchResource(url, resourceName);
124:
125: final ElementHandle[] handles = new ElementHandle[1];
126:
127: if (fo != null) {
128: final String className = clazz;
129: JavaSource js = JavaSource.forFileObject(fo);
130:
131: try {
132: js.runUserActionTask(
133: new CancellableTask<CompilationController>() {
134:
135: public void cancel() {
136: }
137:
138: public void run(CompilationController ctrl)
139: throws Exception {
140: ctrl.toPhase(Phase.ELEMENTS_RESOLVED);
141: TypeElement classElm = findClass(ctrl,
142: className);
143: if (classElm == null) {
144: // bad luck
145: } else if (member == null) {
146: handles[0] = ElementHandle
147: .create(classElm);
148: } else {
149: int pi = member.indexOf('(');
150: if (pi == -1) {
151: // we are looking for fields
152: handles[0] = findField(
153: classElm, member);
154: } else {
155: // We are looking for method or constructor
156: handles[0] = findMethod(ctrl,
157: classElm, member);
158: }
159: }
160: }
161: }, true);
162: } catch (IOException ex) {
163: Logger.getLogger(SrcFinder.class.getName()).log(
164: Level.SEVERE, ex.getLocalizedMessage(), ex);
165: }
166:
167: }
168: return handles[0] != null ? new Object[] { fo, handles[0] }
169: : null;
170: }
171:
172: /**
173: * searches the file corresponding to javadoc url on all source path.
174: * {@link GlobalPathRegistry#findResource}
175: * is insufficient due to returning just the first occurrence of the file. So having
176: * two platforms installed would bring troubles.
177: * @param url javadoc
178: * @param respath resource in form java/lang/Character.java
179: * @return the file
180: */
181: private static FileObject searchResource(URL url, String respath) {
182: FileObject res = searchBinaryPath(ClassPath.BOOT, respath, url);
183:
184: if (res == null) {
185: res = searchBinaryPath(ClassPath.COMPILE, respath, url);
186: }
187:
188: if (res == null) {
189: res = searchSourcePath(respath, url);
190: }
191:
192: return res;
193:
194: }
195:
196: private static FileObject searchBinaryPath(String classPathID,
197: String respath, URL url) {
198: Set<ClassPath> cpaths = GlobalPathRegistry.getDefault()
199: .getPaths(classPathID);
200: for (ClassPath cpath : cpaths) {
201: FileObject[] cpRoots = cpath.getRoots();
202: for (int i = 0; i < cpRoots.length; i++) {
203: SourceForBinaryQuery.Result result = SourceForBinaryQuery
204: .findSourceRoots(URLMapper.findURL(cpRoots[i],
205: URLMapper.EXTERNAL));
206: FileObject[] srcRoots = result.getRoots();
207: for (int j = 0; j < srcRoots.length; j++) {
208: FileObject fo = srcRoots[j].getFileObject(respath);
209: if (fo != null && isJavadocAssigned(cpath, url)) {
210: return fo;
211: }
212: }
213: }
214: }
215: return null;
216: }
217:
218: private static FileObject searchSourcePath(String respath, URL url) {
219: Set<ClassPath> cpaths = GlobalPathRegistry.getDefault()
220: .getPaths(ClassPath.SOURCE);
221: for (ClassPath cpath : cpaths) {
222: FileObject fo = cpath.findResource(respath);
223: if (fo != null && isJavadocAssigned(cpath, url)) {
224: return fo;
225: }
226: }
227:
228: return null;
229: }
230:
231: /**
232: * checks if the javadoc url is assigned to a given classpath
233: * @param cpath classpath
234: * @param url javadoc
235: * @return is assigned?
236: */
237: private static boolean isJavadocAssigned(ClassPath cpath, URL url) {
238: FileObject[] cpRoots = cpath.getRoots();
239: String urlPath = url.toExternalForm();
240: for (int i = 0; i < cpRoots.length; i++) {
241: JavadocForBinaryQuery.Result result = JavadocForBinaryQuery
242: .findJavadoc(URLMapper.findURL(cpRoots[i],
243: URLMapper.EXTERNAL));
244: URL[] jdRoots = result.getRoots();
245: for (int j = 0; j < jdRoots.length; j++) {
246: String jdRootPath = jdRoots[j].toExternalForm();
247: if (urlPath.indexOf(jdRootPath) >= 0) {
248: return true;
249: }
250: }
251: }
252: return false;
253: }
254:
255: private static TypeElement findClass(CompilationController ctrl,
256: String className) {
257: CompilationUnitTree cunit = ctrl.getCompilationUnit();
258: for (Tree declTree : cunit.getTypeDecls()) {
259: ClassTree classTree = (ClassTree) declTree;
260: if (className.equals(classTree.getSimpleName().toString())) {
261: Trees trees = ctrl.getTrees();
262: TypeElement classElm = (TypeElement) trees
263: .getElement(trees.getPath(cunit, classTree));
264: return classElm;
265: }
266: }
267: return null;
268: }
269:
270: private static ElementHandle findField(TypeElement classElm,
271: String name) {
272: for (Element elm : classElm.getEnclosedElements()) {
273: if (elm.getKind() == ElementKind.FIELD
274: && name.equals(elm.getSimpleName().toString())) {
275: return ElementHandle.create(elm);
276: }
277: }
278: return null;
279: }
280:
281: /** Gets the method we are looking for
282: */
283: private static ElementHandle findMethod(CompilationController ctrl,
284: TypeElement ce, String member) {
285: TreeUtilities utils = ctrl.getTreeUtilities();
286:
287: int pi = member.indexOf('(');
288: String name = member.substring(0, pi);
289:
290: StringTokenizer tokenizer = new StringTokenizer(member
291: .substring(pi), " ,()"); // NOI18N
292: List<TypeMirror> paramList = new ArrayList<TypeMirror>();
293:
294: while (tokenizer.hasMoreTokens()) {
295: String token = tokenizer.nextToken();
296: if (token.endsWith("...")) { // NOI18N
297: // translate varargs to array
298: token = token.substring(0, token.length() - 3);
299: token += "[]"; // NOI18N
300: }
301: TypeMirror type = utils.parseType(token, ce);
302: paramList.add(type);
303: }
304:
305: // search method or constructor
306: for (Element e : ce.getEnclosedElements()) {
307: if (e.getKind() == ElementKind.METHOD
308: && name.equals(e.getSimpleName().toString())
309: && compareTypes(ctrl, paramList,
310: ((ExecutableElement) e).getParameters())) {
311: return ElementHandle.create(e);
312: } else if (e.getKind() == ElementKind.CONSTRUCTOR
313: && name.equals(ce.getSimpleName().toString())
314: && compareTypes(ctrl, paramList,
315: ((ExecutableElement) e).getParameters())) {
316: return ElementHandle.create(e);
317: }
318: }
319:
320: return null;
321: }
322:
323: private static boolean compareTypes(CompilationController ctrl,
324: List<TypeMirror> types,
325: List<? extends VariableElement> params) {
326: if (types.size() != params.size()) {
327: return false;
328: }
329:
330: Iterator<? extends VariableElement> itParams = params
331: .iterator();
332: Iterator<TypeMirror> itTypes = types.iterator();
333: while (itParams.hasNext()) {
334: VariableElement varEl = itParams.next();
335: TypeMirror paramType = varEl.asType();
336: TypeMirror type = itTypes.next();
337:
338: // check types are the same kind
339: if (type.getKind() != paramType.getKind()) {
340: return false;
341: }
342:
343: // check elements since javadoc ignores generics
344: if (type.getKind() == TypeKind.DECLARED) {
345: Element paramElm = ((DeclaredType) paramType)
346: .asElement();
347: Element typeElm = ((DeclaredType) type).asElement();
348: if (paramElm != typeElm) {
349: return false;
350: }
351: } else if (!ctrl.getTypes().isSameType(type, paramType)) { // arrays, primitives
352: return false;
353: }
354:
355: }
356: return true;
357: }
358:
359: }
|