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-2007 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: package org.netbeans.modules.refactoring.java.plugins;
042:
043: import com.sun.source.tree.ClassTree;
044: import com.sun.source.tree.Tree;
045: import com.sun.source.util.TreePath;
046: import java.io.IOException;
047: import java.net.URL;
048: import java.text.MessageFormat;
049: import java.util.*;
050: import org.netbeans.api.fileinfo.NonRecursiveFolder;
051: import org.netbeans.api.java.classpath.ClassPath;
052: import org.netbeans.api.java.source.*;
053: import org.netbeans.api.project.FileOwnerQuery;
054: import org.netbeans.api.project.Project;
055: import org.netbeans.api.project.ProjectUtils;
056: import org.netbeans.api.queries.VisibilityQuery;
057: import org.netbeans.modules.refactoring.api.*;
058: import org.netbeans.modules.refactoring.java.*;
059: import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
060: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
061: import org.openide.filesystems.FileObject;
062: import org.openide.filesystems.FileUtil;
063: import org.openide.filesystems.URLMapper;
064: import org.openide.util.Exceptions;
065: import org.openide.util.NbBundle;
066:
067: public class MoveRefactoringPlugin extends JavaRefactoringPlugin {
068:
069: private Map packagePostfix = new HashMap();
070: AbstractRefactoring refactoring;
071: ArrayList<FileObject> filesToMove = new ArrayList<FileObject>();
072: HashMap<FileObject, ElementHandle> classes;
073: Map<FileObject, Set<FileObject>> whoReferences = new HashMap<FileObject, Set<FileObject>>();
074:
075: public MoveRefactoringPlugin(MoveRefactoring move) {
076: this .refactoring = move;
077: setup(move.getRefactoringSource().lookupAll(FileObject.class),
078: "", true);
079: }
080:
081: public MoveRefactoringPlugin(RenameRefactoring rename) {
082: this .refactoring = rename;
083: FileObject fo = rename.getRefactoringSource().lookup(
084: FileObject.class);
085: if (fo != null) {
086: setup(Collections.singletonList(fo), "", true);
087: } else {
088: setup(Collections.singletonList((rename
089: .getRefactoringSource()
090: .lookup(NonRecursiveFolder.class)).getFolder()),
091: "", false); // NOI18N
092: }
093: }
094:
095: @Override
096: public Problem preCheck() {
097: Problem preCheckProblem = null;
098: for (FileObject file : filesToMove) {
099: if (!RetoucheUtils.isElementInOpenProject(file)) {
100: preCheckProblem = createProblem(preCheckProblem, true,
101: NbBundle.getMessage(
102: MoveRefactoringPlugin.class,
103: "ERR_ProjectNotOpened"));
104: }
105: }
106: return preCheckProblem;
107: }
108:
109: @Override
110: public Problem checkParameters() {
111: return null;
112: }
113:
114: @Override
115: public Problem fastCheckParameters() {
116: if (refactoring instanceof RenameRefactoring) {
117: //folder rename
118: FileObject f = refactoring.getRefactoringSource().lookup(
119: FileObject.class);
120: if (f != null) {
121: String newName = ((RenameRefactoring) refactoring)
122: .getNewName();
123: if (!RetoucheUtils.isValidPackageName(newName)) {
124: String msg = new MessageFormat(NbBundle.getMessage(
125: RenameRefactoringPlugin.class,
126: "ERR_InvalidPackage"))
127: .format(new Object[] { newName });
128: return new Problem(true, msg);
129: }
130:
131: if (f.getParent().getFileObject(newName, f.getExt()) != null) {
132: String msg = new MessageFormat(NbBundle.getMessage(
133: RenameRefactoringPlugin.class,
134: "ERR_PackageExists"))
135: .format(new Object[] { newName });
136: return new Problem(true, msg);
137: }
138: }
139: return super .fastCheckParameters();
140: }
141: if (refactoring instanceof MoveRefactoring) {
142: try {
143: for (FileObject f : filesToMove) {
144: if (!RetoucheUtils.isJavaFile(f))
145: continue;
146: String targetPackageName = this
147: .getTargetPackageName(f);
148: if (!RetoucheUtils
149: .isValidPackageName(targetPackageName)) {
150: String s = NbBundle.getMessage(
151: RenameRefactoringPlugin.class,
152: "ERR_InvalidPackage"); //NOI18N
153: String msg = new MessageFormat(s)
154: .format(new Object[] { targetPackageName });
155: return new Problem(true, msg);
156: }
157: FileObject targetRoot = RetoucheUtils
158: .getClassPathRoot(((MoveRefactoring) refactoring)
159: .getTarget().lookup(URL.class));
160: FileObject targetF = targetRoot
161: .getFileObject(targetPackageName.replace(
162: '.', '/'));
163:
164: String pkgName = null;
165: if ((targetF != null && !targetF.canWrite())) {
166: return new Problem(true, new MessageFormat(
167: NbBundle.getMessage(
168: MoveRefactoringPlugin.class,
169: "ERR_PackageIsReadOnly"))
170: .format( // NOI18N
171: new Object[] { targetPackageName }));
172: }
173:
174: // this.movingToDefaultPackageMap.put(r, Boolean.valueOf(targetF!= null && targetF.equals(classPath.findOwnerRoot(targetF))));
175: pkgName = targetPackageName;
176:
177: if (pkgName == null) {
178: pkgName = ""; // NOI18N
179: } else if (pkgName.length() > 0) {
180: pkgName = pkgName + '.';
181: }
182: //targetPrefix = pkgName;
183:
184: // JavaClass[] sourceClasses = (JavaClass[]) sourceClassesMap.get(r);
185: // String[] names = new String [sourceClasses.length];
186: // for (int x = 0; x < names.length; x++) {
187: // names [x] = sourceClasses [x].getName();
188: // }
189: //
190: // FileObject movedFile = JavaMetamodel.getManager().getDataObject(r).getPrimaryFile();
191: String fileName = f.getName();
192: if (targetF != null) {
193: FileObject[] children = targetF.getChildren();
194: for (int x = 0; x < children.length; x++) {
195: if (children[x].getName().equals(fileName)
196: && "java".equals(children[x]
197: .getExt())
198: && !children[x].equals(f)
199: && !children[x].isVirtual()) { //NOI18N
200: return new Problem(
201: true,
202: new MessageFormat(
203: NbBundle
204: .getMessage(
205: MoveRefactoringPlugin.class,
206: "ERR_ClassToMoveClashes"))
207: .format(new Object[] { fileName } // NOI18N
208: ));
209: }
210: } // for
211: }
212:
213: // boolean accessedByOriginalPackage = ((Boolean) accessedByOriginalPackageMap.get(r)).booleanValue();
214: // boolean movingToDefaultPackage = ((Boolean) movingToDefaultPackageMap.get(r)).booleanValue();
215: // if (p==null && accessedByOriginalPackage && movingToDefaultPackage) {
216: // p= new Problem(false, getString("ERR_MovingClassToDefaultPackage")); // NOI18N
217: // }
218:
219: // if (f.getFolder().getPrimaryFile().equals(targetF) && isPackageCorrect(r)) {
220: // return new Problem(true, getString("ERR_CannotMoveIntoSamePackage"));
221: // }
222: }
223: } catch (IOException ioe) {
224: //do nothing
225: }
226: }
227: return super .fastCheckParameters();
228: }
229:
230: private Problem checkProjectDeps(Set<FileObject> a) {
231: if (refactoring instanceof MoveRefactoring) {
232: Set<FileObject> sourceRoots = new HashSet<FileObject>();
233: for (FileObject file : filesToMove) {
234: ClassPath cp = ClassPath.getClassPath(file,
235: ClassPath.SOURCE);
236: if (cp != null) {
237: FileObject root = cp.findOwnerRoot(file);
238: sourceRoots.add(root);
239: }
240: }
241: URL target = ((MoveRefactoring) refactoring).getTarget()
242: .lookup(URL.class);
243: if (target == null) {
244: return null;
245: }
246: try {
247: FileObject r = RetoucheUtils.getClassPathRoot(target);
248: URL targetUrl = URLMapper
249: .findURL(r, URLMapper.EXTERNAL);
250: Set<URL> deps = SourceUtils
251: .getDependentRoots(targetUrl);
252: for (FileObject sourceRoot : sourceRoots) {
253: URL sourceUrl = URLMapper.findURL(sourceRoot,
254: URLMapper.INTERNAL);
255: if (!deps.contains(sourceUrl)) {
256: Project sourceProject = FileOwnerQuery
257: .getOwner(sourceRoot);
258: for (FileObject affected : a) {
259: if (FileOwnerQuery.getOwner(affected)
260: .equals(sourceProject)
261: && !filesToMove.contains(affected)) {
262: Project targetProject = FileOwnerQuery
263: .getOwner(r);
264: assert sourceProject != null;
265: assert targetProject != null;
266: String sourceName = ProjectUtils
267: .getInformation(sourceProject)
268: .getDisplayName();
269: String targetName = ProjectUtils
270: .getInformation(targetProject)
271: .getDisplayName();
272: return createProblem(
273: null,
274: false,
275: NbBundle
276: .getMessage(
277: MoveRefactoringPlugin.class,
278: "ERR_MissingProjectDeps",
279: sourceName,
280: targetName));
281: }
282: }
283: }
284: }
285: } catch (IOException iOException) {
286: Exceptions.printStackTrace(iOException);
287: }
288: }
289: return null;
290: }
291:
292: private Set<FileObject> getRelevantFiles() {
293: ClasspathInfo cpInfo = getClasspathInfo(refactoring);
294: ClassIndex idx = cpInfo.getClassIndex();
295: Set<FileObject> set = new HashSet<FileObject>();
296: for (Map.Entry<FileObject, ElementHandle> entry : classes
297: .entrySet()) {
298: //set.add(SourceUtils.getFile(el, cpInfo));
299: Set<FileObject> files = idx.getResources(entry.getValue(),
300: EnumSet.of(ClassIndex.SearchKind.TYPE_REFERENCES,
301: ClassIndex.SearchKind.IMPLEMENTORS),
302: EnumSet.of(ClassIndex.SearchScope.SOURCE));
303: set.addAll(files);
304: whoReferences.put(entry.getKey(), files);
305: }
306: set.addAll(filesToMove);
307: return set;
308: }
309:
310: private void initClasses() {
311: classes = new HashMap<FileObject, ElementHandle>();
312: for (int i = 0; i < filesToMove.size(); i++) {
313: final int j = i;
314: try {
315: JavaSource source = JavaSource
316: .forFileObject(filesToMove.get(i));
317:
318: source.runUserActionTask(
319: new CancellableTask<CompilationController>() {
320:
321: public void cancel() {
322: throw new UnsupportedOperationException(
323: "Not supported yet."); // NOI18N
324: }
325:
326: public void run(
327: final CompilationController parameter)
328: throws Exception {
329: parameter
330: .toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
331: List<? extends Tree> trees = parameter
332: .getCompilationUnit()
333: .getTypeDecls();
334: for (Tree t : trees) {
335: if (t.getKind() == Tree.Kind.CLASS) {
336: if (((ClassTree) t)
337: .getSimpleName()
338: .toString()
339: .equals(
340: filesToMove
341: .get(j)
342: .getName())) {
343: classes
344: .put(
345: filesToMove
346: .get(j),
347: ElementHandle
348: .create(parameter
349: .getTrees()
350: .getElement(
351: TreePath
352: .getPath(
353: parameter
354: .getCompilationUnit(),
355: t))));
356: return;
357: }
358: }
359: }
360:
361: }
362: }, true);
363: } catch (IOException ex) {
364: java.util.logging.Logger.getLogger("global").log(
365: java.util.logging.Level.SEVERE,
366: ex.getMessage(), ex);
367: }
368:
369: }
370: }
371:
372: public Problem prepare(RefactoringElementsBag elements) {
373: initClasses();
374:
375: Set<FileObject> a = getRelevantFiles();
376: Problem p = checkProjectDeps(a);
377: fireProgressListenerStart(ProgressEvent.START, a.size());
378: MoveTransformer t;
379: TransformTask task = new TransformTask(t = new MoveTransformer(
380: this ), null);
381: createAndAddElements(a, task, elements, refactoring);
382: fireProgressListenerStop();
383: p = chainProblems(p, t.getProblem());
384: return p;
385: }
386:
387: private static Problem chainProblems(Problem p, Problem p1) {
388: Problem problem;
389:
390: if (p == null)
391: return p1;
392: if (p1 == null)
393: return p;
394: problem = p;
395: while (problem.getNext() != null) {
396: problem = problem.getNext();
397: }
398: problem.setNext(p1);
399: return p;
400: }
401:
402: String getNewPackageName() {
403: if (refactoring instanceof MoveRefactoring) {
404: return RetoucheUtils
405: .getPackageName(((MoveRefactoring) refactoring)
406: .getTarget().lookup(URL.class));
407: } else {
408: return ((RenameRefactoring) refactoring).getNewName();
409: }
410: }
411:
412: String getTargetPackageName(FileObject fo) {
413: if (refactoring instanceof RenameRefactoring) {
414: if (refactoring.getRefactoringSource().lookup(
415: NonRecursiveFolder.class) != null)
416: //package rename
417: return getNewPackageName();
418: else {
419: //folder rename
420: FileObject folder = refactoring.getRefactoringSource()
421: .lookup(FileObject.class);
422: ClassPath cp = ClassPath.getClassPath(folder,
423: ClassPath.SOURCE);
424: FileObject root = cp.findOwnerRoot(folder);
425: String prefix = FileUtil.getRelativePath(root,
426: folder.getParent()).replace('/', '.');
427: String postfix = FileUtil.getRelativePath(folder,
428: fo.getParent()).replace('/', '.');
429: String t = concat(prefix, getNewPackageName(), postfix);
430: return t;
431: }
432: } else if (packagePostfix != null) {
433: String postfix = (String) packagePostfix.get(fo);
434: String packageName = concat(null, getNewPackageName(),
435: postfix);
436: return packageName;
437: } else
438: return getNewPackageName();
439: }
440:
441: private void setup(Collection fileObjects, String postfix,
442: boolean recursively) {
443: for (Iterator i = fileObjects.iterator(); i.hasNext();) {
444: FileObject fo = (FileObject) i.next();
445: if (RetoucheUtils.isJavaFile(fo)) {
446: packagePostfix.put(fo, postfix.replace('/', '.'));
447: filesToMove.add(fo);
448: } else if (!(fo.isFolder())) {
449: packagePostfix.put(fo, postfix.replace('/', '.'));
450: } else if (VisibilityQuery.getDefault().isVisible(fo)) {
451: //o instanceof DataFolder
452: //CVS folders are ignored
453: boolean addDot = !"".equals(postfix);
454: Collection col = new ArrayList();
455: for (FileObject fo2 : fo.getChildren()) {
456: if (!fo2.isFolder()
457: || (fo2.isFolder() && recursively))
458: col.add(fo2);
459: }
460: setup(col,
461: postfix + (addDot ? "." : "") + fo.getName(),
462: recursively); // NOI18N
463: }
464: }
465: }
466:
467: private String concat(String s1, String s2, String s3) {
468: String result = "";
469: if (s1 != null && !"".equals(s1)) {
470: result += s1 + "."; // NOI18N
471: }
472: result += s2;
473: if (s3 != null && !"".equals(s3)) {
474: result += ("".equals(result) ? "" : ".") + s3; // NOI18N
475: }
476: return result;
477: }
478:
479: protected JavaSource getJavaSource(Phase p) {
480: return null;
481: }
482: }
|