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.IOException;
045: import org.netbeans.api.fileinfo.NonRecursiveFolder;
046: import org.netbeans.api.java.project.JavaProjectConstants;
047: import org.netbeans.api.java.source.TreePathHandle;
048: import org.netbeans.api.project.FileOwnerQuery;
049: import org.netbeans.api.project.Project;
050: import org.netbeans.api.project.SourceGroup;
051: import org.netbeans.api.project.Sources;
052: import org.netbeans.modules.apisupport.project.layers.LayerUtils;
053: import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
054: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
055: import org.netbeans.modules.refactoring.api.Problem;
056: import org.netbeans.modules.refactoring.api.RenameRefactoring;
057: import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
058: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
059: import org.openide.ErrorManager;
060: import org.openide.filesystems.FileLock;
061: import org.openide.filesystems.FileObject;
062: import org.openide.filesystems.FileUtil;
063: import org.openide.util.Exceptions;
064: import org.openide.util.Lookup;
065: import org.openide.util.NbBundle;
066:
067: /**
068: *
069: * @author Milos Kleint - inspired by j2eerefactoring
070: */
071: public class NbRenameRefactoringPlugin extends
072: AbstractRefactoringPlugin {
073:
074: /** This one is important creature - makes sure that cycles between plugins won't appear */
075: private static ThreadLocal semafor = new ThreadLocal();
076: private RenameRefactoring rename;
077:
078: /**
079: * Creates a new instance of NbRenameRefactoringPlugin
080: */
081: public NbRenameRefactoringPlugin(AbstractRefactoring refactoring) {
082: super (refactoring);
083: }
084:
085: public void cancelRequest() {
086:
087: }
088:
089: public Problem fastCheckParameters() {
090: return null;
091: }
092:
093: /** Collects refactoring elements for a given refactoring.
094: * @param refactoringElements Collection of refactoring elements - the implementation of this method
095: * should add refactoring elements to this collections. It should make no assumptions about the collection
096: * content.
097: * @return Problems found or null (if no problems were identified)
098: */
099: public Problem prepare(RefactoringElementsBag refactoringElements) {
100: if (semafor.get() != null) {
101: return null;
102: }
103: semafor.set(new Object());
104: try {
105: rename = (RenameRefactoring) refactoring;
106:
107: Problem problem = null;
108: Lookup lkp = rename.getRefactoringSource();
109:
110: TreePathHandle handle = lkp.lookup(TreePathHandle.class);
111: if (handle != null) {
112: InfoHolder infoholder = examineLookup(lkp);
113: Project project = FileOwnerQuery.getOwner(handle
114: .getFileObject());
115: if (project == null
116: || project.getLookup().lookup(
117: NbModuleProvider.class) == null) {
118: // take just netbeans module development into account..
119: return null;
120: }
121:
122: if (infoholder.isClass) {
123: checkManifest(project, infoholder.fullName,
124: refactoringElements);
125: checkMetaInfServices(project, infoholder.fullName,
126: refactoringElements);
127: checkLayer(project, infoholder.fullName,
128: refactoringElements);
129: }
130: if (infoholder.isMethod) {
131: checkMethodLayer(infoholder,
132: handle.getFileObject(), refactoringElements);
133: }
134: }
135: NonRecursiveFolder nrf = lkp
136: .lookup(NonRecursiveFolder.class);
137: if (nrf != null) {
138: Project project = FileOwnerQuery.getOwner(nrf
139: .getFolder());
140: if (project.getLookup().lookup(NbModuleProvider.class) == null) {
141: // take just netbeans module development into account..
142: return null;
143: }
144: Sources srcs = org.netbeans.api.project.ProjectUtils
145: .getSources(project);
146: SourceGroup[] grps = srcs
147: .getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
148: for (SourceGroup gr : grps) {
149: if (FileUtil.isParentOf(gr.getRootFolder(), nrf
150: .getFolder())) {
151: String relPath = FileUtil.getRelativePath(gr
152: .getRootFolder(), nrf.getFolder());
153: relPath.replace('/', '.');
154: checkPackageMetaInfServices(project, relPath,
155: refactoringElements);
156: //TODO probably aslo manifest or layers.
157: //TODO how to distinguish single package from recursive folder?
158: }
159: }
160: }
161:
162: err.log("Gonna return problem: " + problem);
163: return problem;
164: } catch (IOException ex) {
165: Exceptions.printStackTrace(ex);
166: return null;
167: } finally {
168: semafor.set(null);
169: }
170: }
171:
172: protected RefactoringElementImplementation createManifestRefactoring(
173: String fqname, FileObject manifestFile,
174: String attributeKey, String attributeValue, String section) {
175: return new ManifestRenameRefactoringElement(fqname,
176: manifestFile, attributeValue, attributeKey, section);
177: }
178:
179: protected RefactoringElementImplementation createMetaInfServicesRefactoring(
180: String clazz, FileObject serviceFile, int line) {
181: return new ServicesRenameRefactoringElement(clazz, serviceFile);
182: }
183:
184: protected final void checkPackageMetaInfServices(Project project,
185: String pack, RefactoringElementsBag refactoringElements) {
186: FileObject services = Utility.findMetaInfServices(project);
187: if (services == null) {
188: return;
189: }
190:
191: String name = pack;
192: // Easiest to check them all; otherwise would need to find all interfaces and superclasses:
193: FileObject[] files = services.getChildren();
194: for (int i = 0; i < files.length; i++) {
195: int line = checkContentOfFile(files[i], name);
196: if (line != -1) {
197: RefactoringElementImplementation elem = new ServicesPackageRenameRefactoringElement(
198: pack, files[i]);
199: if (elem != null) {
200: refactoringElements.add(refactoring, elem);
201: }
202: }
203: }
204: }
205:
206: protected RefactoringElementImplementation createConstructorLayerRefactoring(
207: String constructor, String fqname,
208: LayerUtils.LayerHandle handle, FileObject layerFileObject,
209: String layerAttribute) {
210: // cannot rename a constructor.. is always a class rename
211: return null;
212: }
213:
214: protected RefactoringElementImplementation createLayerRefactoring(
215: String fqname, LayerUtils.LayerHandle handle,
216: FileObject layerFileObject, String layerAttribute) {
217: return new LayerClassRefactoringElement(fqname, handle,
218: layerFileObject, layerAttribute);
219: }
220:
221: protected RefactoringElementImplementation createMethodLayerRefactoring(
222: String method, String fqname,
223: LayerUtils.LayerHandle handle, FileObject layerFileObject,
224: String layerAttribute) {
225: return new LayerMethodRefactoringElement(method, handle,
226: layerFileObject, layerAttribute);
227: }
228:
229: public abstract class LayerAbstractRefactoringElement extends
230: AbstractRefactoringElement {
231:
232: protected FileObject layerFile;
233: protected LayerUtils.LayerHandle handle;
234: protected String oldFileName;
235: protected String oldAttrName;
236: protected String oldAttrValue;
237: protected String valueType;
238:
239: public LayerAbstractRefactoringElement(
240: LayerUtils.LayerHandle handle, FileObject layerFile,
241: String attributeName) {
242: this .layerFile = layerFile;
243: this .parentFile = handle.getLayerFile();
244: this .handle = handle;
245:
246: oldFileName = layerFile.getName();
247: oldAttrName = attributeName;
248: if (attributeName != null) {
249: Object val = layerFile.getAttribute("literal:"
250: + attributeName);
251: if (val == null) {
252: throw new IllegalStateException();
253: }
254: if (val instanceof String) {
255: oldAttrValue = (String) val;
256: if (oldAttrValue.startsWith("new:")) {
257: oldAttrValue = ((String) val).substring("new:"
258: .length());
259: valueType = "newvalue:";
260: } else if (oldAttrValue.startsWith("method:")) {
261: oldAttrValue = ((String) val)
262: .substring("method:".length());
263: valueType = "methodvalue:";
264: }
265: }
266: }
267: }
268:
269: protected void doAttributeValueChange(String newOne, String type) {
270: boolean on = handle.isAutosave();
271: if (!on) {
272: //TODO is this a hack or not?
273: handle.setAutosave(true);
274: }
275: try {
276: layerFile.setAttribute(oldAttrName,
277: (type != null ? type : "") + newOne);
278: } catch (IOException exc) {
279: ErrorManager.getDefault().notify(exc);
280: }
281: if (!on) {
282: handle.setAutosave(false);
283: }
284: }
285:
286: protected void doAttributeMove(String oldKey, String newKey) {
287: boolean on = handle.isAutosave();
288: if (!on) {
289: //TODO is this a hack or not?
290: handle.setAutosave(true);
291: }
292: try {
293: Object obj = layerFile.getAttribute(oldKey);
294: // now assume we're just moving ordering attributes..
295: layerFile.setAttribute(oldKey, null);
296: layerFile.setAttribute(newKey, obj);
297: } catch (IOException exc) {
298: ErrorManager.getDefault().notify(exc);
299: }
300: if (!on) {
301: handle.setAutosave(false);
302: }
303: }
304:
305: protected void doFileMove(String newName) {
306: boolean on = handle.isAutosave();
307: if (!on) {
308: //TODO is this a hack or not?
309: handle.setAutosave(true);
310: }
311: FileLock lock = null;
312: try {
313: lock = layerFile.lock();
314: layerFile.rename(lock, newName, layerFile.getExt());
315: } catch (IOException exc) {
316: ErrorManager.getDefault().notify(exc);
317: } finally {
318: if (lock != null) {
319: lock.releaseLock();
320: }
321: }
322: if (!on) {
323: handle.setAutosave(false);
324: }
325: }
326: }
327:
328: public final class LayerMethodRefactoringElement extends
329: LayerAbstractRefactoringElement {
330:
331: private String newAttrValue;
332: private String method;
333:
334: public LayerMethodRefactoringElement(String method,
335: LayerUtils.LayerHandle handle, FileObject layerFile,
336: String attributeName) {
337: super (handle, layerFile, attributeName);
338: this .method = method;
339: }
340:
341: /** Returns text describing the refactoring formatted for display (using HTML tags).
342: * @return Formatted text.
343: */
344: public String getDisplayText() {
345: return NbBundle.getMessage(getClass(),
346: "TXT_LayerMethodRename", oldAttrValue, rename
347: .getNewName());
348: }
349:
350: public void performChange() {
351: // for methods the change can only be in the attribute value;
352: newAttrValue = oldAttrValue.replaceAll(
353: "\\." + method + "$", "." + rename.getNewName());
354: doAttributeValueChange(newAttrValue, valueType);
355: }
356:
357: public void undoChange() {
358: doAttributeValueChange(oldAttrValue, valueType);
359: }
360:
361: }
362:
363: public final class LayerClassRefactoringElement extends
364: LayerAbstractRefactoringElement {
365:
366: private String fqname;
367: private String newAttrName;
368: private String newAttrValue;
369: private String newFileName;
370:
371: public LayerClassRefactoringElement(String fqname,
372: LayerUtils.LayerHandle handle, FileObject layerFile,
373: String attributeName) {
374: super (handle, layerFile, attributeName);
375: this .fqname = fqname;
376: }
377:
378: /** Returns text describing the refactoring formatted for display (using HTML tags).
379: * @return Formatted text.
380: */
381: public String getDisplayText() {
382: if (newFileName != null) {
383: return NbBundle.getMessage(getClass(),
384: "TXT_LayerFileRename", oldFileName, rename
385: .getNewName());
386: }
387: return NbBundle.getMessage(getClass(),
388: "TXT_LayerMethodRename", oldAttrValue, rename
389: .getNewName());
390: }
391:
392: public void performChange() {
393: // for classes the change can be anywhere;
394: String nm = fqname.substring(fqname.lastIndexOf('.') + 1);
395: if (oldAttrName == null) {
396: // no attribute -> it's a filename change. eg. org-milos-kleint-MyInstance.instance
397: newFileName = oldFileName.replaceAll("\\-" + nm + "$",
398: "-" + rename.getNewName());
399: } else {
400: if (oldAttrName.indexOf(fqname.replace('.', '-')
401: + ".instance") > 0) {
402: //replacing the ordering attribute..
403: newAttrName = oldAttrName.replaceAll("-" + nm
404: + "\\.", "-" + rename.getNewName() + ".");
405: } else {
406: //replacing attr value probably in instanceCreate and similar
407: if (oldAttrValue != null) {
408: String toReplacePattern = nm;
409: newAttrValue = oldAttrValue.replaceAll(
410: toReplacePattern, rename.getNewName());
411: }
412: }
413: }
414:
415: if (newAttrValue != null) {
416: doAttributeValueChange(newAttrValue, valueType);
417: }
418: if (newAttrName != null) {
419: doAttributeMove(oldAttrName, newAttrName);
420: }
421: if (newFileName != null) {
422: doFileMove(newFileName);
423: }
424: }
425:
426: public void undoChange() {
427: if (newAttrValue != null) {
428: doAttributeValueChange(oldAttrValue, valueType);
429: }
430: if (newAttrName != null) {
431: doAttributeMove(newAttrName, oldAttrName);
432: }
433: if (newFileName != null) {
434: doFileMove(oldFileName);
435: }
436: }
437:
438: }
439:
440: public final class ManifestRenameRefactoringElement extends
441: AbstractRefactoringElement {
442:
443: private String attrName;
444: private String sectionName = null;
445: private String oldName;
446: private String oldContent;
447: private String newName;
448:
449: public ManifestRenameRefactoringElement(String fqname,
450: FileObject parentFile, String attributeValue,
451: String attributeName) {
452: this .name = attributeValue;
453: this .parentFile = parentFile;
454: attrName = attributeName;
455: oldName = fqname;
456: }
457:
458: public ManifestRenameRefactoringElement(String fqname,
459: FileObject parentFile, String attributeValue,
460: String attributeName, String secName) {
461: this (fqname, parentFile, attributeValue, attributeName);
462: sectionName = secName;
463: }
464:
465: /** Returns text describing the refactoring formatted for display (using HTML tags).
466: * @return Formatted text.
467: */
468: public String getDisplayText() {
469: if (sectionName != null) {
470: return NbBundle.getMessage(
471: NbRenameRefactoringPlugin.class,
472: "TXT_ManifestSectionRename", this .name,
473: sectionName);
474: }
475: return NbBundle.getMessage(NbRenameRefactoringPlugin.class,
476: "TXT_ManifestRename", this .name, attrName);
477: }
478:
479: @Override
480: public void performChange() {
481: String content = Utility.readFileIntoString(parentFile);
482: oldContent = content;
483: if (content != null) {
484: String shortName = oldName.substring(oldName
485: .lastIndexOf(".") + 1);
486: if (newName == null) {
487: newName = rename.getNewName();
488: newName = newName.replace('.', '/') + ".class"; //NOI18N
489: }
490: shortName = shortName + ".class"; //NOI18N
491: content = content.replaceAll(shortName, newName);
492: Utility.writeFileFromString(parentFile, content);
493: }
494: }
495:
496: @Override
497: public void undoChange() {
498: if (oldContent != null) {
499: Utility.writeFileFromString(parentFile, oldContent);
500: }
501: }
502: }
503:
504: public final class ServicesRenameRefactoringElement extends
505: AbstractRefactoringElement {
506:
507: private String oldName;
508: private String oldContent;
509: private String newName;
510:
511: /**
512: * Creates a new instance of ServicesRenameRefactoringElement
513: */
514: public ServicesRenameRefactoringElement(String fqname,
515: FileObject file) {
516: this .name = fqname.substring(fqname.lastIndexOf('.') + 1);
517: parentFile = file;
518: oldName = fqname;
519: }
520:
521: /** Returns text describing the refactoring formatted for display (using HTML tags).
522: * @return Formatted text.
523: */
524: public String getDisplayText() {
525: return NbBundle.getMessage(NbRenameRefactoringPlugin.class,
526: "TXT_ServicesRename", this .name);
527: }
528:
529: public void performChange() {
530: String content = Utility.readFileIntoString(parentFile);
531: oldContent = content;
532: if (content != null) {
533: if (newName == null) {
534: // System.out.println("new name=" + rename.getNewName());
535: newName = rename.getNewName();
536: }
537: content = content.replaceAll(name + "[ \\\r\\\n]*",
538: newName + System.getProperty("line.separator")); // NOI18N
539: Utility.writeFileFromString(parentFile, content);
540: }
541: }
542:
543: @Override
544: public void undoChange() {
545: if (oldContent != null) {
546: Utility.writeFileFromString(parentFile, oldContent);
547: }
548: }
549: }
550:
551: public final class ServicesPackageRenameRefactoringElement extends
552: AbstractRefactoringElement {
553:
554: private String pack;
555: private String oldName;
556:
557: /**
558: * Creates a new instance of ServicesRenameRefactoringElement
559: */
560: public ServicesPackageRenameRefactoringElement(String pack,
561: FileObject file) {
562: this .name = pack;
563: parentFile = file;
564: this .pack = pack;
565: oldName = pack;
566: }
567:
568: /** Returns text describing the refactoring formatted for display (using HTML tags).
569: * @return Formatted text.
570: */
571: public String getDisplayText() {
572: return NbBundle.getMessage(NbRenameRefactoringPlugin.class,
573: "TXT_ServicesPackageRename", this .name);
574: }
575:
576: @Override
577: public void performChange() {
578: String content = Utility.readFileIntoString(parentFile);
579: if (content != null) {
580: String longName = oldName;
581: String newName = rename.getNewName();
582: longName = longName.replaceAll("[.]", "\\."); // NOI18N
583: content = content.replaceAll("^" + longName, newName); // NOI18N
584: Utility.writeFileFromString(parentFile, content);
585: }
586: }
587:
588: public void undoChange() {
589: //TODO
590: }
591:
592: }
593:
594: }
|