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.apisupport.refactoring;
043:
044: import java.io.BufferedReader;
045: import java.io.IOException;
046: import java.io.InputStreamReader;
047: import java.util.Enumeration;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Map;
051: import java.util.jar.Attributes;
052: import java.util.jar.Manifest;
053: import java.util.regex.Matcher;
054: import java.util.regex.Pattern;
055: import javax.lang.model.element.Element;
056: import javax.lang.model.element.ElementKind;
057: import javax.lang.model.element.ExecutableElement;
058: import javax.lang.model.element.Modifier;
059: import javax.lang.model.element.TypeElement;
060: import javax.lang.model.element.VariableElement;
061: import javax.lang.model.type.DeclaredType;
062: import javax.lang.model.type.TypeKind;
063: import javax.lang.model.type.TypeMirror;
064: import org.netbeans.api.java.source.CancellableTask;
065: import org.netbeans.api.java.source.CompilationController;
066: import org.netbeans.api.java.source.JavaSource;
067: import org.netbeans.api.java.source.TreePathHandle;
068: import org.netbeans.api.project.FileOwnerQuery;
069: import org.netbeans.api.project.Project;
070: import org.netbeans.modules.apisupport.project.Util;
071: import org.netbeans.modules.apisupport.project.layers.LayerUtils;
072: import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
073: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
074: import org.netbeans.modules.refactoring.api.Problem;
075: import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
076: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
077: import org.netbeans.modules.refactoring.spi.RefactoringPlugin;
078: import org.openide.ErrorManager;
079: import org.openide.filesystems.FileObject;
080: import org.openide.filesystems.FileSystem;
081: import org.openide.util.Lookup;
082:
083: /**
084: *
085: * @author mkleint
086: */
087: public abstract class AbstractRefactoringPlugin implements
088: RefactoringPlugin {
089: /**
090: *
091: */
092: protected static ErrorManager err = ErrorManager.getDefault()
093: .getInstance("org.netbeans.modules.apisupport.refactoring"); // NOI18N
094:
095: /**
096: *
097: */
098: protected AbstractRefactoring refactoring;
099: // a regexp pattern for ordering attributes
100: /**
101: *
102: */
103: protected Pattern orderingLayerAttrPattern = Pattern
104: .compile("([\\S]+)/([\\S]+)"); //NOI18N
105:
106: /** Creates a new instance of AbstractRefactoringPlugin */
107: public AbstractRefactoringPlugin(AbstractRefactoring refactoring) {
108: this .refactoring = refactoring;
109: }
110:
111: /** Checks pre-conditions of the refactoring and returns problems.
112: * @return Problems found or null (if no problems were identified)
113: */
114: public Problem preCheck() {
115: return null;
116: }
117:
118: /** Checks parameters of the refactoring.
119: * @return Problems found or null (if no problems were identified)
120: */
121: public Problem checkParameters() {
122: return null;
123: }
124:
125: /**
126: * returns the line number in the file if found, otherwise -1
127: */
128: protected final int checkContentOfFile(FileObject fo,
129: String classToLookFor) {
130: BufferedReader reader = null;
131: try {
132: reader = new BufferedReader(new InputStreamReader(fo
133: .getInputStream(), "UTF-8")); // NOI18N
134: String line = reader.readLine();
135: int counter = 0;
136: while (line != null) {
137: if (line.indexOf(classToLookFor) != -1) {
138: return counter;
139: }
140: counter = counter + 1;
141: line = reader.readLine();
142: }
143: } catch (IOException exc) {
144: //TODO
145: } finally {
146: if (reader != null) {
147: try {
148: reader.close();
149: } catch (IOException x) {
150: // ignore
151: }
152: }
153: }
154: return -1;
155: }
156:
157: protected static class InfoHolder {
158: public String name = null;
159: public String fullName = null;
160: public boolean isClass = false;
161: public boolean isMethod = false;
162: public boolean isConstructor = false;
163: public boolean isPublic = false;
164: public boolean isStatic = false;
165: public boolean hasFileObjectParam = false;
166: public boolean hasNoParams = false;
167: }
168:
169: protected final InfoHolder examineLookup(Lookup lkp)
170: throws IOException {
171: final TreePathHandle handle = lkp.lookup(TreePathHandle.class);
172: final InfoHolder infoholder = new InfoHolder();
173: JavaSource source = JavaSource.forFileObject(handle
174: .getFileObject());
175:
176: CancellableTask<CompilationController> info = new CancellableTask<CompilationController>() {
177: public void run(CompilationController info)
178: throws Exception {
179: info.toPhase(JavaSource.Phase.RESOLVED);
180: Element neco = handle.resolveElement(info);
181: infoholder.name = neco.getSimpleName().toString();
182: if (neco.getKind() == ElementKind.CLASS) {
183: infoholder.isClass = true;
184: TypeElement te = (TypeElement) neco;
185: infoholder.fullName = te.getQualifiedName()
186: .toString();
187: } else if (neco.getKind() == ElementKind.METHOD) {
188: infoholder.isMethod = true;
189: ExecutableElement ee = (ExecutableElement) neco;
190: TypeElement te = (TypeElement) ee
191: .getEnclosingElement();
192: infoholder.fullName = te.getQualifiedName()
193: .toString();
194: infoholder.isPublic = ee.getModifiers().contains(
195: Modifier.PUBLIC);
196: infoholder.isStatic = ee.getModifiers().contains(
197: Modifier.STATIC);
198: List<? extends VariableElement> lst = ee
199: .getParameters();
200: if (lst.size() > 1) {
201: infoholder.hasFileObjectParam = false;
202: } else {
203: if (lst.size() == 0) {
204: infoholder.hasNoParams = true;
205: }
206: for (VariableElement el : lst) {
207: TypeMirror tm = el.asType();
208: if (tm.getKind() == TypeKind.DECLARED) {
209: TypeElement vare = (TypeElement) ((DeclaredType) tm)
210: .asElement();
211: String fqn = vare.getQualifiedName()
212: .toString();
213: if ("org.openide.filesystems.FileObject"
214: .equals(fqn)) {
215: infoholder.hasFileObjectParam = true;
216: }
217: }
218: }
219: }
220:
221: } else if (neco.getKind() == ElementKind.CONSTRUCTOR) {
222: infoholder.isConstructor = true;
223: ExecutableElement ee = (ExecutableElement) neco;
224: TypeElement te = (TypeElement) ee
225: .getEnclosingElement();
226: infoholder.fullName = te.getQualifiedName()
227: .toString();
228: infoholder.isPublic = ee.getModifiers().contains(
229: Modifier.PUBLIC);
230: List<? extends VariableElement> lst = ee
231: .getParameters();
232: if (lst.size() == 0) {
233: infoholder.hasNoParams = true;
234: }
235: }
236: }
237:
238: public void cancel() {
239: }
240: };
241: source.runUserActionTask(info, true);
242: return infoholder;
243: }
244:
245: /**
246: *
247: * @param project
248: * @param fqClassName
249: * @param refactoringElements
250: */
251: protected final void checkManifest(Project project,
252: String fqClassName,
253: RefactoringElementsBag refactoringElements) {
254: String name = fqClassName;
255: NbModuleProvider prov = project.getLookup().lookup(
256: NbModuleProvider.class);
257: if (prov == null) {
258: return;
259: }
260: String pathName = name.replace('.', '/') + ".class"; //NOI18N
261: Manifest mf = Util.getManifest(prov.getManifestFile());
262: if (mf == null) {
263: return;
264: }
265: Attributes attrs = mf.getMainAttributes();
266: Iterator it = attrs.entrySet().iterator();
267: while (it.hasNext()) {
268: Map.Entry entry = (Map.Entry) it.next();
269: String val = (String) entry.getValue();
270: if (val.indexOf(name) != -1 || val.indexOf(pathName) != -1) {
271: RefactoringElementImplementation elem = createManifestRefactoring(
272: name, prov.getManifestFile(),
273: ((Attributes.Name) entry.getKey()).toString(),
274: val, null);
275: if (elem != null) {
276: refactoringElements.add(refactoring, elem);
277: }
278: }
279: }
280: Map entries = mf.getEntries();
281: if (entries != null) {
282: it = entries.entrySet().iterator();
283: while (it.hasNext()) {
284: Map.Entry secEnt = (Map.Entry) it.next();
285: attrs = (Attributes) secEnt.getValue();
286: String val = (String) secEnt.getKey();
287: if (val.indexOf(name) != -1
288: || val.indexOf(pathName) != -1) {
289: String section = attrs
290: .getValue("OpenIDE-Module-Class"); //NOI18N
291: RefactoringElementImplementation elem = createManifestRefactoring(
292: name, prov.getManifestFile(), null, val,
293: section);
294: if (elem != null) {
295: refactoringElements.add(refactoring, elem);
296: }
297: }
298: }
299: }
300: }
301:
302: protected final void checkMetaInfServices(Project project,
303: String fqname, RefactoringElementsBag refactoringElements) {
304: FileObject services = Utility.findMetaInfServices(project);
305: if (services == null) {
306: return;
307: }
308: String name = fqname;
309: // Easiest to check them all; otherwise would need to find all interfaces and superclasses:
310: FileObject[] files = services.getChildren();
311: for (int i = 0; i < files.length; i++) {
312: int line = checkContentOfFile(files[i], name);
313: if (line != -1) {
314: RefactoringElementImplementation elem = createMetaInfServicesRefactoring(
315: fqname, files[i], line);
316: if (elem != null) {
317: refactoringElements.add(refactoring, elem);
318: }
319: }
320: }
321: }
322:
323: protected final void checkLayer(Project project, String fqname,
324: RefactoringElementsBag refactoringElements) {
325: LayerUtils.LayerHandle handle = LayerUtils
326: .layerForProject(project);
327: FileSystem fs = handle.layer(false);
328: if (fs != null) {
329: checkFileObject(fs.getRoot(), fqname, refactoringElements,
330: handle);
331: }
332: }
333:
334: private void checkFileObject(FileObject fo, String fqname,
335: RefactoringElementsBag refactoringElements,
336: LayerUtils.LayerHandle handle) {
337: if (fo.isFolder()) {
338: FileObject[] childs = fo.getChildren();
339: for (int i = 0; i < childs.length; i++) {
340: checkFileObject(childs[i], fqname, refactoringElements,
341: handle);
342: }
343: Enumeration en = fo.getAttributes();
344: // check ordering attributes?
345: while (en.hasMoreElements()) {
346: String attrKey = (String) en.nextElement();
347: Matcher match = orderingLayerAttrPattern
348: .matcher(attrKey);
349: if (match.matches()) {
350: String first = match.group(1);
351: if (first.endsWith(".instance")) { //NOI18N
352: String name = first.substring(0,
353: first.length() - ".instance".length())
354: .replace('-', '.'); //NOI18N
355: if (name.equals(fqname)) {
356: RefactoringElementImplementation elem = createLayerRefactoring(
357: fqname, handle, fo, attrKey);
358: if (elem != null) {
359: refactoringElements.add(refactoring,
360: elem);
361: }
362: }
363: }
364: String second = match.group(2);
365: if (second.endsWith(".instance")) { //NOI18N
366: String name = second.substring(0,
367: second.length() - ".instance".length())
368: .replace('-', '.'); //NOI18N
369: if (name.equals(fqname)) {
370: RefactoringElementImplementation elem = createLayerRefactoring(
371: fqname, handle, fo, attrKey);
372: if (elem != null) {
373: refactoringElements.add(refactoring,
374: elem);
375: }
376: }
377: }
378: }
379: }
380: } else if (fo.isData()) {
381:
382: Enumeration en = fo.getAttributes();
383: // check just a few specific attributes or iterate all?
384: while (en.hasMoreElements()) {
385: String attrKey = (String) en.nextElement();
386: Object val = fo.getAttribute("literal:" + attrKey); //NOI18N
387: if (val instanceof String) {
388: String attrValue = (String) val;
389: String value = attrValue;
390: if (attrValue.startsWith("new:")) { //NOI18N
391: value = attrValue.substring("new:".length()); //NOI18N
392: }
393: if (attrValue.startsWith("method:")) { //NOI18N
394: value = attrValue.substring("method:".length()); //NOI18N
395: int index = value.lastIndexOf('.');
396: if (index > 0) {
397: value = value.substring(0, index);
398: }
399: }
400: String pattern1 = fqname.replaceAll("\\.", "\\."); //NOI18N
401: String pattern2 = "[a-zA-Z0-9/-]*"
402: + fqname.replaceAll("\\.", "-")
403: + "\\.instance"; //NOI18N
404:
405: if (value.matches(pattern1)
406: || value.matches(pattern2)) {
407: RefactoringElementImplementation elem = createLayerRefactoring(
408: fqname, handle, fo, attrKey);
409: if (elem != null) {
410: refactoringElements.add(refactoring, elem);
411: }
412: }
413: }
414: }
415: // the actual fileobject is checked after the attributes, so that both can be performed.
416: if ("instance".equals(fo.getExt())) { // NOI18N
417: String name = fo.getName().replace('-', '.');
418: if (name.equals(fqname)) {
419: RefactoringElementImplementation elem = createLayerRefactoring(
420: fqname, handle, fo, null);
421: if (elem != null) {
422: refactoringElements.add(refactoring, elem);
423: }
424: }
425: }
426: if ("settings".equals(fo.getExt())) { // NOI18N
427: //TODO check also content of settings files for matches?
428: }
429:
430: }
431:
432: }
433:
434: protected final Problem checkMethodLayer(InfoHolder info,
435: FileObject fo, RefactoringElementsBag refactoringElements) {
436: Problem problem = null;
437: // do our check just on public static methods..
438: if (!info.isPublic || !info.isStatic) {
439: return problem;
440: }
441: // with no parameters or with parameter of type FileObject
442: if (!info.hasFileObjectParam && !info.hasNoParams) {
443: return problem;
444: }
445: Project project = FileOwnerQuery.getOwner(fo);
446: if (project != null) {
447: LayerUtils.LayerHandle handle = LayerUtils
448: .layerForProject(project);
449: FileSystem fs = handle.layer(false);
450: if (fs != null) {
451: checkFileObject(fs.getRoot(), info.name, null,
452: info.fullName, refactoringElements, handle);
453: }
454: }
455: return problem;
456: }
457:
458: protected final Problem checkConstructorLayer(InfoHolder info,
459: FileObject fo, RefactoringElementsBag refactoringElements) {
460: Problem problem = null;
461: // just consider public constructors with no params..
462: if (!info.isPublic || !info.hasNoParams) {
463: return problem;
464: }
465: Project project = FileOwnerQuery.getOwner(fo);
466: if (project != null) {
467: LayerUtils.LayerHandle handle = LayerUtils
468: .layerForProject(project);
469: FileSystem fs = handle.layer(false);
470: if (fs != null) {
471: checkFileObject(fs.getRoot(), null, info.name,
472: info.fullName, refactoringElements, handle);
473: }
474: }
475: return problem;
476: }
477:
478: private void checkFileObject(FileObject fo, String method,
479: String constructor, String fqname,
480: RefactoringElementsBag refactoringElements,
481: LayerUtils.LayerHandle handle) {
482: if (fo.isFolder()) {
483: FileObject[] childs = fo.getChildren();
484: for (int i = 0; i < childs.length; i++) {
485: checkFileObject(childs[i], method, constructor, fqname,
486: refactoringElements, handle);
487: }
488: } else if (fo.isData()) {
489: if ("settings".equals(fo.getExt())) { // NOI18N
490: //TODO check also content of settings files for matches?
491: }
492: Enumeration en = fo.getAttributes();
493: // check just a few specific attributes or iterate all?
494: while (en.hasMoreElements()) {
495: String attrKey = (String) en.nextElement();
496: Object val = fo.getAttribute("literal:" + attrKey); //NOI18N
497: if (val instanceof String) {
498: String attrValue = (String) val;
499: if (method != null
500: && attrValue.startsWith("method:")
501: && attrValue.endsWith(method)) { //NOI18N
502: String clazz = attrValue.substring("method:"
503: .length()); //NOI18N
504: String methodString = null;
505: int index = clazz.lastIndexOf('.');
506: if (index > 0) {
507: methodString = clazz.substring(index + 1);
508: clazz = clazz.substring(0, index);
509: }
510: if (methodString != null
511: && methodString.equals(method)
512: && clazz.equals(fqname)) {
513: RefactoringElementImplementation elem = createLayerRefactoring(
514: method, handle, fo, attrKey);
515: if (elem != null) {
516: refactoringElements.add(refactoring,
517: elem);
518: }
519: }
520: }
521: if (constructor != null
522: && attrValue.startsWith("new:")) { //NOI18N
523: String clazz = attrValue.substring("new:"
524: .length()); //NOI18N
525: if (clazz.equals(fqname)) {
526: RefactoringElementImplementation elem = createLayerRefactoring(
527: constructor, handle, fo, attrKey);
528: if (elem != null) {
529: refactoringElements.add(refactoring,
530: elem);
531: }
532: }
533: }
534: }
535: }
536: }
537:
538: }
539:
540: protected abstract RefactoringElementImplementation createMetaInfServicesRefactoring(
541: String fqclazz, FileObject serviceFile, int line);
542:
543: /**
544: *
545: * @param manifestFile
546: * @param attributeKey
547: * @param attributeValue
548: * @param section
549: * @return
550: */
551: protected abstract RefactoringElementImplementation createManifestRefactoring(
552: String fqname, FileObject manifestFile,
553: String attributeKey, String attributeValue, String section);
554:
555: protected RefactoringElementImplementation createLayerRefactoring(
556: String fqname, LayerUtils.LayerHandle handle,
557: FileObject layerFileObject, String layerAttribute) {
558: throw new AssertionError(
559: "if you call checkLayer(), you need to implement this method");
560: }
561:
562: protected RefactoringElementImplementation createMethodLayerRefactoring(
563: String method, String fqname,
564: LayerUtils.LayerHandle handle, FileObject layerFileObject,
565: String layerAttribute) {
566: throw new AssertionError(
567: "if you call checkLayer(), you need to implement this method");
568: }
569:
570: protected RefactoringElementImplementation createConstructorLayerRefactoring(
571: String constructor, String fqname,
572: LayerUtils.LayerHandle handle, FileObject layerFileObject,
573: String layerAttribute) {
574: throw new AssertionError(
575: "if you call checkLayer(), you need to implement this method");
576: }
577:
578: }
|