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 java.io.IOException;
044: import java.util.Collection;
045: import java.util.HashSet;
046: import java.util.Set;
047: import javax.lang.model.element.Element;
048: import javax.lang.model.element.ElementKind;
049: import javax.lang.model.element.Modifier;
050: import javax.lang.model.element.TypeElement;
051: import org.netbeans.api.java.source.ClasspathInfo;
052: import org.netbeans.api.java.source.CompilationController;
053: import org.netbeans.api.java.source.ElementHandle;
054: import org.netbeans.api.java.source.JavaSource;
055: import org.netbeans.api.java.source.TreePathHandle;
056: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
057: import org.netbeans.modules.refactoring.api.Problem;
058: import org.netbeans.modules.refactoring.api.ProgressEvent;
059: import org.netbeans.modules.refactoring.java.RetoucheUtils;
060: import org.netbeans.modules.refactoring.java.api.MemberInfo;
061: import org.netbeans.modules.refactoring.java.api.PullUpRefactoring;
062: import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
063: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
064: import org.openide.filesystems.FileObject;
065: import org.openide.util.NbBundle;
066: import org.openide.util.NbBundle;
067:
068: /** Plugin that implements the core functionality of Pull Up refactoring.
069: *
070: * @author Martin Matula
071: * @author Jan Becicka
072: */
073: public final class PullUpRefactoringPlugin extends
074: JavaRefactoringPlugin {
075: /** Reference to the parent refactoring instance */
076: private final PullUpRefactoring refactoring;
077: private TreePathHandle treePathHandle;
078:
079: /** Creates a new instance of PullUpRefactoringPlugin
080: * @param refactoring Parent refactoring instance.
081: */
082: PullUpRefactoringPlugin(PullUpRefactoring refactoring) {
083: this .refactoring = refactoring;
084: this .treePathHandle = refactoring.getSourceType();
085: }
086:
087: protected JavaSource getJavaSource(Phase p) {
088: switch (p) {
089: default:
090: return JavaSource.forFileObject(treePathHandle
091: .getFileObject());
092: }
093: }
094:
095: @Override
096: protected Problem preCheck(CompilationController cc)
097: throws IOException {
098: fireProgressListenerStart(AbstractRefactoring.PRE_CHECK, 4);
099: try {
100: cc.toPhase(JavaSource.Phase.RESOLVED);
101: Problem problem = isElementAvail(treePathHandle, cc);
102: if (problem != null) {
103: // fatal error -> don't continue with further checks
104: return problem;
105: }
106: if (!RetoucheUtils.isElementInOpenProject(treePathHandle
107: .getFileObject())) {
108: return new Problem(true, NbBundle.getMessage(
109: PullUpRefactoringPlugin.class,
110: "ERR_ProjectNotOpened"));
111: }
112:
113: // increase progress (step 1)
114: fireProgressListenerStep();
115: TypeElement e = (TypeElement) treePathHandle
116: .resolveElement(cc);
117: if (RetoucheUtils.getSuperTypes(e, cc, true).isEmpty()) {
118: return new Problem(true, NbBundle.getMessage(
119: PullUpRefactoringPlugin.class,
120: "ERR_PullUp_NoSuperTypes")); // NOI18N
121: }
122: // increase progress (step 2)
123: fireProgressListenerStep();
124: // #2 - check if there are any members to pull up
125: Element el = treePathHandle.resolveElement(cc);
126: for (Element element : el.getEnclosedElements()) {
127: if (element.getKind() != ElementKind.CONSTRUCTOR) {
128: return null;
129: }
130: }
131: problem = new Problem(true, NbBundle.getMessage(
132: PullUpRefactoringPlugin.class,
133: "ERR_PullUp_NoMembers")); // NOI18N
134: // increase progress (step 3)
135: fireProgressListenerStep();
136: return problem;
137: } finally {
138: fireProgressListenerStop();
139: }
140: }
141:
142: @Override
143: public Problem fastCheckParameters() {
144: MemberInfo[] info = refactoring.getMembers();
145: // #1 - check whether there are any members to pull up
146: if (info.length == 0) {
147: return new Problem(true, NbBundle.getMessage(
148: PullUpRefactoringPlugin.class,
149: "ERR_PullUp_NoMembersSelected")); // NOI18N
150: }
151:
152: if (info.length > 1) {
153: for (int i = 0; i < info.length - 1; i++) {
154: for (int j = i + 1; j < info.length; j++) {
155: if (info[i].equals(info[j])) {
156: return new Problem(true, NbBundle.getMessage(
157: PullUpRefactoringPlugin.class,
158: "ERR_CannotPullupDuplicateMembers"));
159: }
160: }
161: }
162: }
163:
164: // #2 - check if the targed type is not null
165: if (refactoring.getTargetType() == null) {
166: return new Problem(true, NbBundle.getMessage(
167: PullUpRefactoringPlugin.class,
168: "ERR_PullUp_NoTargetType")); // NOI18N
169: }
170:
171: Problem p = null;
172: if (refactoring.getTargetType().getKind().isInterface()) {
173: for (MemberInfo i : info) {
174: if (!i.getModifiers().contains(Modifier.PUBLIC)) {
175: p = createProblem(p, false, NbBundle.getMessage(
176: PullUpRefactoringPlugin.class,
177: "ERR_PullupNonPublicToInterface", i
178: .getName()));
179: }
180: }
181: }
182:
183: return p;
184: }
185:
186: @Override
187: protected Problem checkParameters(CompilationController cc)
188: throws IOException {
189: fireProgressListenerStart(AbstractRefactoring.PRE_CHECK, 4);
190: try {
191: cc.toPhase(JavaSource.Phase.RESOLVED);
192: TypeElement sourceType = (TypeElement) refactoring
193: .getSourceType().resolveElement(cc);
194: Collection<TypeElement> super s = RetoucheUtils
195: .getSuperTypes(sourceType, cc);
196: TypeElement targetType = refactoring.getTargetType()
197: .resolve(cc);
198: MemberInfo<ElementHandle<? extends Element>>[] members = refactoring
199: .getMembers();
200:
201: fireProgressListenerStart(
202: AbstractRefactoring.PARAMETERS_CHECK,
203: members.length + 1);
204: // #1 - check whether the target type is a legal super type
205: if (!super s.contains(targetType)) {
206: return new Problem(true, NbBundle.getMessage(
207: PullUpRefactoringPlugin.class,
208: "ERR_PullUp_IllegalTargetType")); // NOI18N
209: }
210:
211: fireProgressListenerStep();
212:
213: // TODO: what the hell is this check
214: // #2 - check whether all the members are legal members that can be pulled up
215: // HashSet visitedSources = new HashSet();
216: // // HashSet allMembers = new HashSet(Arrays.asList(members));
217: Problem problems = null;
218: // visitedSources.add(refactoring.getSourceType());
219: for (int i = 0; i < members.length; i++) {
220: Element cls;
221: Element member = members[i].getElementHandle().resolve(
222: cc);
223: if (members[i].getGroup() != MemberInfo.Group.IMPLEMENTS) {
224: // // member is a feature (inner class, field or method)
225: // cls = member.getEnclosingElement();
226: // } else {
227: // // member is an interface from implements clause
228: // MultipartId ifcName = (MultipartId) member;
229: // // get parent of the element (should be class if this is really
230: // // a name from implements clause
231: // Object parent = ifcName.refImmediateComposite();
232: // // if parent is not a class, member is invalid
233: // if (!(parent instanceof JavaClass)) {
234: // cls = null;
235: // } else {
236: // // check if the parent class contains this MultipartId
237: // // in interfaceNames
238: // if (!((JavaClass) parent).getInterfaceNames().contains(ifcName)) {
239: // cls = null;
240: // } else {
241: // cls = (ClassDefinition) parent;
242: // }
243: // }
244: // }
245: // // if the declaring class has not been visited yet, perform checks on it
246: // if (visitedSources.add(cls)) {
247: // // if the declaring class of a feature is not a JavaClass,
248: // // or if it is not from the set of source type's supertypes
249: // // or if the declaring class is not a subtype of target class
250: // // then this member is illegal
251: // if (!(cls instanceof JavaClass) || !supers.contains(cls) || cls.equals(targetType) || !cls.isSubTypeOf(targetType)) {
252: // return createProblem(problems, true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_IllegalMember", member.getName())); // NOI18N
253: // }
254: // }
255: // #3 - check if the member already exists in the target class
256:
257: if (RetoucheUtils.elementExistsIn(targetType,
258: member, cc)) {
259: return createProblem(
260: problems,
261: true,
262: NbBundle
263: .getMessage(
264: PullUpRefactoringPlugin.class,
265: "ERR_PullUp_MemberAlreadyExists",
266: member.getSimpleName())); // NOI18N
267: }
268:
269: // #4 - check if the field does not use something that is not going to be pulled up
270: // Resource sourceResource = refactoring.getSourceType().getResource();
271: // Resource targetResource = targetType.getResource();
272: // if (!sourceResource.equals(targetResource)) {
273: // problems = checkUsedByElement(member, allMembers, problems,
274: // !sourceResource.equals(targetResource),
275: // !sourceResource.getPackageName().equals(targetResource.getPackageName()));
276: // }
277:
278: fireProgressListenerStep();
279: }
280:
281: // TODO: implement non-fatal checks
282: }
283: } finally {
284: fireProgressListenerStop();
285: }
286: return null;
287: }
288:
289: public Problem prepare(RefactoringElementsBag refactoringElements) {
290: ClasspathInfo cpInfo = getClasspathInfo(refactoring);
291:
292: Set<FileObject> a = new HashSet<FileObject>();
293: a.addAll(RetoucheUtils.getSuperTypesFiles(refactoring
294: .getSourceType()));
295: a.add(RetoucheUtils.getFileObject(treePathHandle));
296: fireProgressListenerStart(ProgressEvent.START, a.size());
297: TransformTask task = new TransformTask(new PullUpTransformer(
298: refactoring), treePathHandle);
299: createAndAddElements(a, task, refactoringElements, refactoring,
300: cpInfo);
301: fireProgressListenerStop();
302: return null;
303: }
304:
305: protected FileObject getFileObject() {
306: return treePathHandle.getFileObject();
307: }
308: }
|