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: package org.netbeans.modules.xml.refactoring.spi;
042:
043: import java.io.IOException;
044: import java.util.ArrayList;
045: import java.util.Arrays;
046: import java.util.Collection;
047: import java.util.Enumeration;
048: import java.util.HashSet;
049: import java.util.List;
050: import java.util.Set;
051: import org.netbeans.api.project.FileOwnerQuery;
052: import org.netbeans.api.project.Project;
053: import org.netbeans.api.project.ProjectUtils;
054: import org.netbeans.api.project.SourceGroup;
055: import org.netbeans.api.project.Sources;
056: import org.netbeans.api.project.ui.OpenProjects;
057: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
058: import org.netbeans.modules.refactoring.api.RenameRefactoring;
059: import org.netbeans.modules.refactoring.api.SafeDeleteRefactoring;
060: import org.netbeans.modules.xml.refactoring.ErrorItem;
061: import org.netbeans.modules.xml.refactoring.ui.GraphHelper;
062: import org.netbeans.modules.xml.retriever.catalog.ProjectCatalogSupport;
063: import org.netbeans.modules.xml.xam.Component;
064: import org.netbeans.modules.xml.xam.Model;
065: import org.netbeans.modules.xml.xam.ModelSource;
066: import org.netbeans.modules.xml.xam.Nameable;
067: import org.netbeans.modules.xml.xam.Referenceable;
068: import org.netbeans.modules.xml.xam.dom.Utils;
069: import org.netbeans.spi.project.SubprojectProvider;
070: import org.openide.cookies.SaveCookie;
071: import org.openide.filesystems.FileObject;
072: import org.openide.loaders.DataObject;
073: import org.openide.util.NbBundle;
074:
075: /**
076: *
077: * @author Nam Nguyen
078: */
079: public class RefactoringUtil {
080:
081: public static final String BUILD = "build";
082:
083: public static Project findCurrentProject(Referenceable referenced) {
084: Model model = referenced instanceof Model ? (Model) referenced
085: : ((Component) referenced).getModel();
086: if (model == null)
087: return null;
088: return FileOwnerQuery.getOwner((FileObject) model
089: .getModelSource().getLookup().lookup(FileObject.class));
090: }
091:
092: public static Set<Project> getReferencingProjects(Project project) {
093: Set<Project> result = new HashSet<Project>();
094: for (Project p : OpenProjects.getDefault().getOpenProjects()) {
095: if (p.getLookup().lookup(ProjectCatalogSupport.class) == null) {
096: continue;
097: }
098:
099: SubprojectProvider spp = (SubprojectProvider) p.getLookup()
100: .lookup(SubprojectProvider.class);
101: if (spp == null)
102: continue;
103: for (Object o : spp.getSubprojects()) {
104: Project sp = (Project) o;
105: if (sp == project) {
106: result.add(p);
107: break;
108: }
109: }
110: }
111:
112: return result;
113: }
114:
115: public static List<SourceGroup> findSourceRoots(Project project) {
116: // get the generic roots so that all roots will be identified
117: SourceGroup[] groups = ProjectUtils.getSources(project)
118: .getSourceGroups(Sources.TYPE_GENERIC);
119: return Arrays.asList(groups);
120: }
121:
122: public static List<FileObject> findSourceFiles(FileObject folder) {
123: Enumeration children = folder.getChildren(true);
124: List<FileObject> ret = new ArrayList<FileObject>();
125: while (children.hasMoreElements()) {
126: FileObject fo = (FileObject) children.nextElement();
127: //need to screen out files under the build directory
128: //if a project is build, then all the files under build are also included in the
129: //search resulting in dup. usages
130: //not sure how to look for files under build dir;
131: //so scanning the complete file path
132: if (fo.isData() && !isBuildDirFile(fo)) {
133: ret.add(fo);
134: }
135: }
136: return ret;
137: }
138:
139: public static boolean isBuildDirFile(FileObject fo) {
140: while (fo != null) {
141: if (fo.getName().equals(BUILD)) {
142: return true;
143: }
144: fo = fo.getParent();
145: }
146: return false;
147: }
148:
149: public static ErrorItem precheckTarget(Model model, boolean autosave) {
150: //Fix for NPE 125763
151: if (model == null)
152: return null;
153: if (model.getState() != Model.State.VALID) {
154: String msg = NbBundle.getMessage(RefactoringUtil.class,
155: "MSG_ModelSourceNotWelformed");
156: return new ErrorItem(model, msg);
157: }
158: if (autosave && !RefactoringUtil.isWritable(model)) {
159: String msg = NbBundle.getMessage(RefactoringUtil.class,
160: "MSG_ModelSourceNotWritable");
161: return new ErrorItem(model, msg);
162: }
163: return null;
164: }
165:
166: public static List<ErrorItem> precheckUsageModels(
167: List<Model> models, boolean autosave) {
168: //Set<Model> models = request.getUsages().getModels();
169: List<ErrorItem> error = new ArrayList<ErrorItem>();
170: for (Model model : models) {
171: if (model.getState() != Model.State.VALID) {
172: String msg = NbBundle.getMessage(RefactoringUtil.class,
173: "MSG_ModelSourceNotWelformed");
174: error.add(new ErrorItem(model, msg));
175: }
176: if (autosave && !RefactoringUtil.isWritable(model)) {
177: String msg = NbBundle.getMessage(RefactoringUtil.class,
178: "MSG_ModelSourceNotWritable");
179: error.add(new ErrorItem(model, msg));
180: }
181: }
182: if (error.size() > 0)
183: return error;
184: else
185: return null;
186: }
187:
188: /* public static void precheckForUnsafeDelete(DeleteRequest request) {
189: if (! request.getUsages().getUsages().isEmpty()) {
190: String msg = NbBundle.getMessage(RefactoringUtil.class, "MSG_UnsafeDelete");
191: request.addError(new ErrorItem(request.getTarget(), msg));
192: }
193: }*/
194:
195: public static String getDescription(AbstractRefactoring request) {
196: Referenceable target = request.getRefactoringSource().lookup(
197: Referenceable.class);
198: if (request instanceof RenameRefactoring
199: && target instanceof Nameable) {
200: return NbBundle.getMessage(RefactoringUtil.class,
201: "LBL_Rename"); //NOI18N
202: } else if (request instanceof SafeDeleteRefactoring) {
203: return NbBundle.getMessage(RefactoringUtil.class,
204: "LBL_Safe_Delete"); //NOI18N
205: } else if (request instanceof RenameRefactoring
206: && target instanceof Model) {
207: return NbBundle.getMessage(RefactoringUtil.class,
208: "LBL_File_Rename"); //NOI18N
209: } else {
210: return ""; //NOI18N
211: }
212: }
213:
214: public static boolean isDirty(Model model) {
215: DataObject obj = (DataObject) model.getModelSource()
216: .getLookup().lookup(DataObject.class);
217: if (obj != null) {
218: return obj.isModified();
219: }
220: return false;
221: }
222:
223: public static DataObject getDataObject(Model model) {
224: if (model != null)
225: return (DataObject) model.getModelSource().getLookup()
226: .lookup(DataObject.class);
227:
228: return null;
229: }
230:
231: public static void saveTargetFile(Model target, Set<Model> all) {
232: Set<Model> excludeds = new HashSet<Model>();
233: excludeds.addAll(all);
234: excludeds.remove(target);
235: save(all, target, excludeds);
236: }
237:
238: public static void save(Set<Model> allModels, Model targetModel,
239: Set<Model> excludeds) {
240: Set<Model> all = new HashSet<Model>();
241: all.addAll(allModels);
242: all.add(targetModel);
243: for (Model model : all) {
244: if (excludeds.contains(model)) {
245: continue;
246: }
247: DataObject obj = getDataObject(model);
248: if (obj == null) {
249: String msg = NbBundle.getMessage(RefactoringUtil.class,
250: "MSG_CannotFindDataObject");
251: // request.addError(new ErrorItem(model, msg));
252: continue;
253: }
254:
255: SaveCookie save = (SaveCookie) obj
256: .getCookie(SaveCookie.class);
257: FileObject fo = obj.getPrimaryFile();
258: if (save != null) {
259: try {
260: save.save();
261: obj.setModified(false);
262: } catch (IOException ioe) {
263: String msg = NbBundle.getMessage(
264: RefactoringUtil.class, "MSG_ErrorSave", fo
265: .getPath(), ioe.getMessage());
266: // request.addError(new ErrorItem(model, msg));
267: continue;
268: }
269: } else {
270: String msg = NbBundle.getMessage(RefactoringUtil.class,
271: "MSG_CannotSave", fo.getPath());
272: // request.addError(new ErrorItem(model, msg));
273: continue;
274: }
275: }
276:
277: }
278:
279: public static String getDescription(GraphHelper.Type type) {
280: switch (type) {
281: case GENERALIZATION:
282: return NbBundle.getMessage(RefactoringUtil.class,
283: "LBL_Generalization");
284: case REFERENCE:
285: return NbBundle.getMessage(RefactoringUtil.class,
286: "LBL_Reference");
287: default:
288: assert false : "Invalid type " + type;
289: return "";
290: }
291: }
292:
293: public static ErrorItem precheck(Nameable target, String newName) {
294: if (newName == null || !Utils.isValidNCName(newName)) {
295: return new ErrorItem(target, NbBundle.getMessage(
296: RefactoringUtil.class, "MSG_NewNameNullEmpty"),
297: ErrorItem.Level.FATAL);
298: } else if (!checkDuplicateName(target, newName)) {
299: return new ErrorItem(target, NbBundle.getMessage(
300: RefactoringUtil.class, "MSG_NewNameDuplicate"),
301: ErrorItem.Level.FATAL);
302: }
303: return null;
304: }
305:
306: public static ErrorItem precheck(Model model, String newName) {
307: FileObject current = model.getModelSource().getLookup().lookup(
308: FileObject.class);
309: assert current != null : "Failed to lookup for file object in model source";
310: FileObject parent = current.getParent();
311: assert (parent != null) : "Source file has no parent folder";
312:
313: if (newName == null
314: || newName.trim().length() == 0
315: || parent.getFileObject(newName, current.getExt()) != null) {
316: return new ErrorItem(model, NbBundle.getMessage(
317: RefactoringUtil.class, "MSG_NewNameDuplicate"),
318: ErrorItem.Level.FATAL);
319: } else if (!Utils.isValidNCName(newName)) {
320: return new ErrorItem(model, NbBundle.getMessage(
321: RefactoringUtil.class, "MSG_NewNameNullEmpty"),
322: ErrorItem.Level.FATAL);
323: }
324: return null;
325: }
326:
327: /**
328: * Returns true if the check result is OK.
329: */
330: public static boolean checkDuplicateName(Nameable component,
331: String newName) {
332: // Nameable component = request.getNameableTarget();
333: Component parent = component.getParent();
334: Collection<Component> siblings = parent.getChildren(component
335: .getClass());
336: for (Component c : siblings) {
337: Nameable nameable = (Nameable) c;
338: if (nameable.getName() != null
339: && nameable.getName().equals(newName)) {
340: return false;
341: }
342: }
343: return true;
344: }
345:
346: public static boolean isWritable(Model model) {
347: if (model != null) {
348: ModelSource ms = model.getModelSource();
349: if (ms.isEditable()) {
350: FileObject fo = (FileObject) ms.getLookup().lookup(
351: FileObject.class);
352: if (fo != null) {
353: return fo.canWrite();
354: }
355: }
356: }
357: return false;
358: }
359:
360: public static Set<Model> getDirtyModels(Set<Model> models,
361: Model targetModel) {
362: HashSet<Model> dirties = new HashSet<Model>();
363: if (RefactoringUtil.isDirty(targetModel)) {
364: dirties.add(targetModel);
365: }
366: for (Model model : models) {
367: if (RefactoringUtil.isDirty(model)) {
368: dirties.add(model);
369: }
370: }
371:
372: return dirties;
373: }
374: }
|