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.refactoring.java.plugins;
043:
044: import org.netbeans.modules.refactoring.java.spi.RefactoringVisitor;
045: import com.sun.source.tree.*;
046: import java.util.HashSet;
047: import java.util.Set;
048: import javax.lang.model.element.*;
049: import org.netbeans.api.java.source.ElementHandle;
050: import org.netbeans.api.java.source.SourceUtils;
051: import org.netbeans.api.java.source.WorkingCopy;
052: import org.netbeans.modules.refactoring.api.Problem;
053: import org.netbeans.modules.refactoring.api.RenameRefactoring;
054: import org.netbeans.modules.refactoring.java.RetoucheUtils;
055: import org.netbeans.modules.refactoring.java.spi.ToPhaseException;
056: import org.openide.filesystems.FileObject;
057: import org.openide.util.NbBundle;
058:
059: /**
060: *
061: * @author Jan Becicka
062: */
063: public class MoveTransformer extends RefactoringVisitor {
064:
065: private FileObject originalFolder;
066: private MoveRefactoringPlugin move;
067: private Set<Element> elementsToImport = new HashSet();
068: private boolean isThisFileMoving;
069: private boolean isThisFileReferencingOldPackage = false;
070: private Set<Element> elementsAlreadyImported = new HashSet();
071: private Problem problem;
072: private boolean moveToDefaulPackageProblem = false;
073:
074: public Problem getProblem() {
075: return problem;
076: }
077:
078: public MoveTransformer(MoveRefactoringPlugin move) {
079: this .move = move;
080: }
081:
082: public void setWorkingCopy(WorkingCopy copy)
083: throws ToPhaseException {
084: super .setWorkingCopy(copy);
085: originalFolder = workingCopy.getFileObject().getParent();
086: isThisFileMoving = move.filesToMove.contains(workingCopy
087: .getFileObject());
088: elementsToImport = new HashSet();
089: isThisFileReferencingOldPackage = false;
090: elementsAlreadyImported = new HashSet();
091: }
092:
093: @Override
094: public Tree visitMemberSelect(MemberSelectTree node, Element p) {
095: if (!workingCopy.getTreeUtilities().isSynthetic(
096: getCurrentPath())) {
097: Element el = workingCopy.getTrees().getElement(
098: getCurrentPath());
099: if (el != null) {
100: FileObject fo = SourceUtils.getFile(el, workingCopy
101: .getClasspathInfo());
102: if (isElementMoving(el)) {
103: elementsAlreadyImported.add(el);
104: String newPackageName = move
105: .getTargetPackageName(SourceUtils.getFile(
106: el, workingCopy.getClasspathInfo()));
107: if (!"".equals(newPackageName)) {
108: Tree nju = make.MemberSelect(make
109: .Identifier(newPackageName), el);
110: rewrite(node, nju);
111: } else {
112: if (!moveToDefaulPackageProblem) {
113: problem = createProblem(
114: problem,
115: false,
116: NbBundle
117: .getMessage(
118: MoveTransformer.class,
119: "ERR_MovingClassToDefaultPackage"));
120: moveToDefaulPackageProblem = true;
121: }
122: }
123: }
124: if (isThisFileMoving && !isElementMoving(el)) {
125: if (el.getKind() != ElementKind.PACKAGE
126: && !move.filesToMove.contains(fo)
127: && getPackageOf(el).toString().equals(
128: RetoucheUtils
129: .getPackageName(workingCopy
130: .getFileObject()
131: .getParent()))
132: && !(el.getModifiers().contains(
133: Modifier.PUBLIC) || el
134: .getModifiers().contains(
135: Modifier.PROTECTED))) {
136: problem = createProblem(
137: problem,
138: false,
139: NbBundle
140: .getMessage(
141: MoveTransformer.class,
142: "ERR_AccessesPackagePrivateFeature2",
143: workingCopy
144: .getFileObject()
145: .getName(),
146: el,
147: getTypeElement(el)
148: .getSimpleName()));
149: }
150: }
151: if (!isThisFileMoving && !isElementMoving(el)) {
152: if (el.getKind() != ElementKind.PACKAGE
153: && move.filesToMove.contains(fo)
154: && getPackageOf(el).toString().equals(
155: RetoucheUtils
156: .getPackageName(workingCopy
157: .getFileObject()
158: .getParent()))
159: && !(el.getModifiers().contains(
160: Modifier.PUBLIC) || el
161: .getModifiers().contains(
162: Modifier.PROTECTED))) {
163: problem = createProblem(
164: problem,
165: false,
166: NbBundle
167: .getMessage(
168: MoveTransformer.class,
169: "ERR_AccessesPackagePrivateFeature",
170: workingCopy
171: .getFileObject()
172: .getName(),
173: el,
174: getTypeElement(el)
175: .getSimpleName()));
176: }
177: }
178: }
179: }
180: return super .visitMemberSelect(node, p);
181: }
182:
183: @Override
184: public Tree visitIdentifier(IdentifierTree node, Element p) {
185: if (!workingCopy.getTreeUtilities().isSynthetic(
186: getCurrentPath())) {
187: Element el = workingCopy.getTrees().getElement(
188: getCurrentPath());
189: if (el != null) {
190: FileObject fo = SourceUtils.getFile(el, workingCopy
191: .getClasspathInfo());
192: if (!isThisFileMoving) {
193: if (isElementMoving(el)) {
194: if (!elementsAlreadyImported.contains(el)) {
195: if (!RetoucheUtils
196: .getPackageName(
197: workingCopy
198: .getCompilationUnit())
199: .equals(
200: move
201: .getTargetPackageName(fo)))
202: elementsToImport.add(el);
203: }
204: } else if (el.getKind() != ElementKind.PACKAGE
205: && move.filesToMove.contains(fo)
206: && getPackageOf(el).toString().equals(
207: RetoucheUtils
208: .getPackageName(workingCopy
209: .getFileObject()
210: .getParent()))
211: && !(el.getModifiers().contains(
212: Modifier.PUBLIC) || el
213: .getModifiers().contains(
214: Modifier.PROTECTED))) {
215: problem = createProblem(
216: problem,
217: false,
218: NbBundle
219: .getMessage(
220: MoveTransformer.class,
221: "ERR_AccessesPackagePrivateFeature",
222: workingCopy
223: .getFileObject()
224: .getName(),
225: el,
226: getTypeElement(el)
227: .getSimpleName()));
228: }
229: } else {
230: if (!isThisFileReferencingOldPackage
231: && (!isElementMoving(el) && isTopLevelClass(el))
232: && getPackageOf(el).toString().equals(
233: RetoucheUtils
234: .getPackageName(workingCopy
235: .getFileObject()
236: .getParent()))) {
237: isThisFileReferencingOldPackage = true;
238: }
239: if (el.getKind() != ElementKind.PACKAGE
240: && (!isElementMoving(el)
241: && !move.filesToMove.contains(fo) && getPackageOf(
242: el).toString().equals(
243: RetoucheUtils
244: .getPackageName(workingCopy
245: .getFileObject()
246: .getParent())))
247: && !(el.getModifiers().contains(
248: Modifier.PUBLIC) || el
249: .getModifiers().contains(
250: Modifier.PROTECTED))) {
251: problem = createProblem(
252: problem,
253: false,
254: NbBundle
255: .getMessage(
256: MoveTransformer.class,
257: "ERR_AccessesPackagePrivateFeature2",
258: workingCopy
259: .getFileObject()
260: .getName(),
261: el,
262: getTypeElement(el)
263: .getSimpleName()));
264: }
265: }
266: }
267: }
268:
269: return super .visitIdentifier(node, p);
270: }
271:
272: private TypeElement getTypeElement(Element e) {
273: TypeElement t = SourceUtils.getEnclosingTypeElement(e);
274: if (t == null && e instanceof TypeElement) {
275: return (TypeElement) e;
276: }
277: return t;
278: }
279:
280: static final Problem createProblem(Problem result, boolean isFatal,
281: String message) {
282: Problem problem = new Problem(isFatal, message);
283: if (result == null) {
284: return problem;
285: }
286: problem.setNext(result);
287: return problem;
288: }
289:
290: private PackageElement getPackageOf(Element el) {
291: //return workingCopy.getElements().getPackageOf(el);
292: while (el.getKind() != ElementKind.PACKAGE)
293: el = el.getEnclosingElement();
294: return (PackageElement) el;
295: }
296:
297: private boolean isPackageRename() {
298: return move.refactoring instanceof RenameRefactoring;
299: }
300:
301: private boolean isThisFileReferencedbyOldPackage() {
302: Set<FileObject> references = new HashSet(move.whoReferences
303: .get(workingCopy.getFileObject()));
304: references.removeAll(move.filesToMove);
305: for (FileObject file : references) {
306: if (file.getParent().equals(originalFolder))
307: return true;
308: }
309: return false;
310: }
311:
312: // private boolean isThisFileReferencingOldPackage() {
313: // //TODO: correctly implement
314: // return true;
315: // }
316:
317: private boolean isElementMoving(Element el) {
318: for (ElementHandle handle : move.classes.values()) {
319: if (handle.signatureEquals(el)) {
320: return true;
321: }
322: }
323: return false;
324: }
325:
326: private boolean isTopLevelClass(Element el) {
327: return (el.getKind().isClass() || el.getKind().isInterface())
328: && el.getEnclosingElement().getKind() == ElementKind.PACKAGE;
329: }
330:
331: @Override
332: public Tree visitCompilationUnit(CompilationUnitTree node, Element p) {
333: Tree result = super .visitCompilationUnit(node, p);
334: if (workingCopy.getTreeUtilities()
335: .isSynthetic(getCurrentPath())) {
336: return result;
337: }
338: CompilationUnitTree cut = node;
339: if (isThisFileMoving) {
340: // change package statement if old and new package exist, i.e.
341: // neither old nor new package is default
342: String newPckg = move.getTargetPackageName(workingCopy
343: .getFileObject());
344: if (node.getPackageName() != null && !"".equals(newPckg)) {
345: rewrite(node.getPackageName(), make.Identifier(move
346: .getTargetPackageName(workingCopy
347: .getFileObject())));
348: } else {
349: // in order to handle default package, we have to rewrite whole
350: // compilation unit:
351: cut = make.CompilationUnit("".equals(newPckg) ? null
352: : make.Identifier(newPckg), node.getImports(),
353: node.getTypeDecls(), node.getSourceFile());
354: }
355: if (isThisFileReferencingOldPackage) {
356: //add import to old package
357: ExpressionTree newPackageName = cut.getPackageName();
358: if (newPackageName != null) {
359: cut = insertImport(cut, newPackageName.toString()
360: + ".*", null); // NOI18N
361: } else {
362: if (!moveToDefaulPackageProblem) {
363: problem = createProblem(
364: problem,
365: false,
366: NbBundle
367: .getMessage(
368: MoveTransformer.class,
369: "ERR_MovingClassToDefaultPackage"));
370: moveToDefaulPackageProblem = true;
371: }
372: }
373:
374: }
375: }
376: for (Element el : elementsToImport) {
377: FileObject fo = SourceUtils.getFile(el, workingCopy
378: .getClasspathInfo());
379: String newPackageName = move.getTargetPackageName(fo);
380: if (!"".equals(newPackageName)) {
381: cut = insertImport(cut, newPackageName + "."
382: + el.getSimpleName(), el); // NOI18N
383: }
384: }
385: rewrite(node, cut);
386: return result;
387: }
388:
389: private CompilationUnitTree insertImport(CompilationUnitTree node,
390: String imp, Element orig) {
391: for (ImportTree tree : node.getImports()) {
392: if (tree.getQualifiedIdentifier().toString().equals(imp))
393: return node;
394: if (orig != null) {
395: if (tree.getQualifiedIdentifier().toString().equals(
396: getPackageOf(orig).getQualifiedName() + ".*")
397: && isPackageRename()) { // NOI18N
398: FileObject fo = SourceUtils.getFile(orig,
399: workingCopy.getClasspathInfo());
400: rewrite(tree.getQualifiedIdentifier(), make
401: .Identifier(move.getTargetPackageName(fo)
402: + ".*")); // NOI18N
403: return node;
404: }
405: }
406: }
407: CompilationUnitTree nju = make.insertCompUnitImport(node, 0,
408: make.Import(make.Identifier(imp), false));
409: return nju;
410: }
411:
412: }
|