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.j2ee.jpa.refactoring;
042:
043: import com.sun.source.tree.CompilationUnitTree;
044: import com.sun.source.util.TreePath;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.Collection;
048: import java.util.Collections;
049: import java.util.HashSet;
050: import java.util.List;
051: import java.util.Set;
052: import javax.lang.model.element.Element;
053: import javax.lang.model.element.ElementKind;
054: import org.netbeans.api.fileinfo.NonRecursiveFolder;
055: import org.netbeans.api.java.source.TreePathHandle;
056: import org.netbeans.modules.j2ee.persistence.dd.PersistenceUtils;
057: import org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.PersistenceUnit;
058: import org.netbeans.modules.j2ee.persistence.provider.InvalidPersistenceXmlException;
059: import org.netbeans.modules.j2ee.persistence.provider.ProviderUtil;
060: import org.netbeans.modules.j2ee.persistence.unit.PUDataObject;
061: import org.netbeans.modules.refactoring.api.Problem;
062: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
063: import org.openide.filesystems.FileObject;
064: import org.openide.text.PositionBounds;
065: import org.openide.util.NbBundle;
066: import org.netbeans.api.project.FileOwnerQuery;
067: import org.netbeans.api.project.Project;
068: import org.netbeans.modules.j2ee.persistence.api.PersistenceScope;
069: import org.netbeans.api.java.source.CancellableTask;
070: import org.netbeans.api.java.source.CompilationController;
071: import org.netbeans.api.java.source.JavaSource;
072: import org.netbeans.modules.j2ee.core.api.support.java.JavaIdentifiers;
073: import org.netbeans.modules.j2ee.jpa.refactoring.util.PositionBoundsResolver;
074: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
075: import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
076: import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImplementation;
077: import org.openide.loaders.DataObject;
078: import org.openide.loaders.DataObjectNotFoundException;
079: import org.openide.util.Exceptions;
080: import org.openide.util.Lookup;
081: import org.openide.util.lookup.Lookups;
082:
083: /**
084: * A base class for persistence.xml refactorings.
085: *
086: * @author Erno Mononen
087: */
088: public abstract class PersistenceXmlRefactoring implements
089: JPARefactoring {
090:
091: /**
092: * The file objects representing the refactoring sources.
093: */
094: private Collection<? extends FileObject> refactoringSources;
095:
096: /**
097: * Gets the refactoring sources for this refactoring. Move class and safe delete
098: * refactorings may be invoked on multiple files, for other refactorings there
099: * is be just one refactoring source.
100: *
101: *@return the file objects representing the refactoring sources, i.e. the objects
102: * being refactored.
103: */
104: protected Collection<? extends FileObject> getRefactoringSources() {
105: if (refactoringSources == null) {
106: refactoringSources = lookupRefactoringSources();
107: }
108: return refactoringSources;
109: }
110:
111: private Collection<? extends FileObject> lookupRefactoringSources() {
112: // move class and safe delete refactorings may be invoked on multiple files
113: Collection<? extends FileObject> fosFromLookup = getRefactoring()
114: .getRefactoringSource().lookupAll(FileObject.class);
115: if (!fosFromLookup.isEmpty()) {
116: List<FileObject> result = new ArrayList<FileObject>();
117: for (FileObject each : fosFromLookup) {
118: if (each.isFolder()) {
119: collectChildren(each, result);
120: } else {
121: result.add(each);
122: }
123: }
124: return result;
125: }
126: NonRecursiveFolder folder = getRefactoring()
127: .getRefactoringSource()
128: .lookup(NonRecursiveFolder.class);
129: if (folder != null) {
130: return Collections.singleton(folder.getFolder());
131: }
132:
133: TreePathHandle treePathHandle = getRefactoring()
134: .getRefactoringSource().lookup(TreePathHandle.class);
135: if (treePathHandle != null) {
136: return Collections
137: .singleton(treePathHandle.getFileObject());
138: }
139:
140: return Collections.<FileObject> emptySet();
141: }
142:
143: /**
144: * Recursively collects the java files from the given folder into the
145: * given <code>result</code>.
146: */
147: public static void collectChildren(FileObject folder,
148: List<FileObject> result) {
149: for (FileObject child : folder.getChildren()) {
150: if (RefactoringUtil.isJavaFile(child)) {
151: result.add(child);
152: } else if (child.isFolder()) {
153: collectChildren(child, result);
154: }
155: }
156: }
157:
158: /**
159: * Checks whether any of the objects being refactored should be handled by
160: * this refactoring. Override in subclasses as needed, the
161: * default implementation returns true if any of the refactored objects
162: * is a Java class.
163: *
164: * @return true if the any of the refactoring sources represents a Java class.
165: */
166: protected boolean shouldHandle() {
167:
168: for (FileObject refactoringSource : getRefactoringSources()) {
169: if (shouldHandle(refactoringSource)) {
170: return true;
171: }
172: }
173: return false;
174:
175: }
176:
177: /**
178: * Checks whether the given <code>refactoringSource</code> should be handled by
179: * this refactoring. Override in subclasses as needed, the
180: * default implementation returns true if the given <code>refactoringSource</code>
181: * is a class.
182: * @param refactoringSource the object being refactored.
183: *
184: * @return true if the <code>refactoringSource<code> represents a class that
185: * should be handled by persistence.xml refactorings.
186: */
187: protected boolean shouldHandle(FileObject refactoringSource) {
188: final boolean[] result = new boolean[] { false };
189:
190: if (RefactoringUtil.isJavaFile(refactoringSource)) {
191: JavaSource source = JavaSource
192: .forFileObject(refactoringSource);
193: try {
194: source.runUserActionTask(
195: new CancellableTask<CompilationController>() {
196:
197: public void cancel() {
198: }
199:
200: public void run(CompilationController info)
201: throws Exception {
202: info.toPhase(JavaSource.Phase.RESOLVED);
203: TreePathHandle treePathHandle = null;
204: CompilationUnitTree cut = info
205: .getCompilationUnit();
206: if (!cut.getTypeDecls().isEmpty()) {
207: treePathHandle = TreePathHandle
208: .create(TreePath.getPath(
209: cut,
210: cut.getTypeDecls()
211: .get(0)),
212: info);
213: }
214: if (treePathHandle == null) {
215: result[0] = false;
216: } else {
217: Element element = treePathHandle
218: .resolveElement(info);
219: if (element != null) {
220: result[0] = element.getKind() == ElementKind.CLASS;
221: }
222: }
223: }
224: }, true);
225: } catch (IOException ex) {
226: Exceptions.printStackTrace(ex);
227: }
228: }
229:
230: return result[0];
231: }
232:
233: public final Problem preCheck() {
234:
235: if (!shouldHandle()) {
236: return null;
237: }
238:
239: Problem result = null;
240:
241: for (FileObject refactoringSource : getRefactoringSources()) {
242: for (FileObject persistenceXml : getPersistenceXmls(refactoringSource)) {
243: try {
244: ProviderUtil.getPUDataObject(persistenceXml);
245: } catch (InvalidPersistenceXmlException ex) {
246: Problem newProblem = new Problem(false, NbBundle
247: .getMessage(
248: PersistenceXmlRefactoring.class,
249: "TXT_PersistenceXmlInvalidProblem",
250: ex.getPath()));
251: result = RefactoringUtil.addToEnd(newProblem,
252: result);
253: }
254: }
255: }
256: return result;
257:
258: }
259:
260: public Problem prepare(RefactoringElementsBag refactoringElementsBag) {
261:
262: if (!shouldHandle()) {
263: return null;
264: }
265:
266: Problem result = null;
267: for (FileObject refactoringSource : getRefactoringSources()) {
268: Project project = FileOwnerQuery
269: .getOwner(refactoringSource);
270: if (project == null) {
271: continue;
272: }
273: if (!shouldHandle(refactoringSource)) {
274: continue;
275: }
276: String classNameFQN = JavaIdentifiers
277: .getQualifiedName(refactoringSource);
278:
279: for (FileObject each : getPersistenceXmls(refactoringSource)) {
280: try {
281: PUDataObject pUDataObject = ProviderUtil
282: .getPUDataObject(each);
283: List<PersistenceUnit> punits = getAffectedPersistenceUnits(
284: pUDataObject, classNameFQN);
285: for (PersistenceUnit persistenceUnit : punits) {
286: refactoringElementsBag.add(getRefactoring(),
287: getRefactoringElement(persistenceUnit,
288: refactoringSource,
289: pUDataObject, each));
290: }
291: } catch (InvalidPersistenceXmlException ex) {
292: Problem newProblem = new Problem(false, NbBundle
293: .getMessage(
294: PersistenceXmlRefactoring.class,
295: "TXT_PersistenceXmlInvalidProblem",
296: ex.getPath()));
297:
298: result = RefactoringUtil.addToEnd(newProblem,
299: result);
300: }
301: }
302: }
303:
304: return result;
305: }
306:
307: /**
308: * @return the actual refactoring being performed.
309: */
310: protected abstract AbstractRefactoring getRefactoring();
311:
312: /**
313: *@return the refactoring element for the given parameters.
314: */
315: protected abstract RefactoringElementImplementation getRefactoringElement(
316: PersistenceUnit persistenceUnit, FileObject clazz,
317: PUDataObject pUDataObject, FileObject persistenceXml);
318:
319: /**
320: * Gets the persistence unit from the given <code>PUDataObject</code> that contain
321: * a class matching with the given <code>clazz</code>.
322: * @param puDataObject
323: * @param clazz the fully qualified name of the class
324: *
325: * @return the persistence units that contain the given class.
326: */
327: protected final List<PersistenceUnit> getAffectedPersistenceUnits(
328: PUDataObject pUDataObject, String clazz) {
329: List<PersistenceUnit> result = new ArrayList<PersistenceUnit>();
330: PersistenceUnit[] persistenceUnits = ProviderUtil
331: .getPersistenceUnits(pUDataObject);
332: for (PersistenceUnit each : persistenceUnits) {
333: if (hasClass(each, clazz)) {
334: result.add(each);
335: }
336: }
337: return result;
338: }
339:
340: private static boolean hasClass(PersistenceUnit persistenceUnit,
341: String clazz) {
342: for (String each : persistenceUnit.getClass2()) {
343: if (each.equals(clazz)) {
344: return true;
345: }
346: }
347: return false;
348: }
349:
350: /**
351: * Gets the persistence.xml files in the project to which the given
352: * <code>refactoringSource</code> belongs.
353: * @param refactoringSource
354: * @return the persistence.xml files in the project to which the refactored
355: * class belongs or an empty list if the class does not belong to any project.
356: */
357: protected final List<FileObject> getPersistenceXmls(
358: FileObject refactoringSource) {
359: Project project = FileOwnerQuery.getOwner(refactoringSource);
360: if (project == null) {
361: return Collections.<FileObject> emptyList();
362: }
363:
364: List<FileObject> result = new ArrayList<FileObject>();
365:
366: PersistenceScope[] persistenceScopes = PersistenceUtils
367: .getPersistenceScopes(project);
368: for (int i = 0; i < persistenceScopes.length; i++) {
369: result.add(persistenceScopes[i].getPersistenceXml());
370: }
371:
372: return result;
373: }
374:
375: protected abstract static class PersistenceXmlRefactoringElement
376: extends SimpleRefactoringElementImplementation {
377:
378: protected final PersistenceUnit persistenceUnit;
379: protected final PUDataObject puDataObject;
380: protected final String clazz;
381: protected final FileObject parentFile;
382:
383: protected PersistenceXmlRefactoringElement(
384: PersistenceUnit persistenceUnit, String clazz,
385: PUDataObject puDataObject, FileObject parentFile) {
386: this .clazz = clazz;
387: this .persistenceUnit = persistenceUnit;
388: this .puDataObject = puDataObject;
389: this .parentFile = parentFile;
390: }
391:
392: public final String getText() {
393: return getDisplayText();
394: }
395:
396: public final Lookup getLookup() {
397: return Lookups.singleton(parentFile);
398: }
399:
400: public final FileObject getParentFile() {
401: return parentFile;
402: }
403:
404: public final PositionBounds getPosition() {
405: try {
406: return new PositionBoundsResolver(DataObject
407: .find(parentFile), clazz).getPositionBounds();
408: } catch (DataObjectNotFoundException ex) {
409: Exceptions.printStackTrace(ex);
410: }
411: return null;
412: }
413: }
414:
415: }
|