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:
042: package org.netbeans.modules.visualweb.complib;
043:
044: import java.io.IOException;
045: import java.util.ArrayList;
046: import java.util.Arrays;
047: import java.util.LinkedList;
048: import java.util.List;
049: import java.util.WeakHashMap;
050:
051: import org.netbeans.modules.visualweb.complib.Complib.Identifier;
052: import org.netbeans.modules.visualweb.complib.ComplibManifest.EeSpecVersion;
053: import org.netbeans.modules.visualweb.complib.ComplibServiceProvider.ComponentInfo;
054: import org.netbeans.modules.visualweb.complib.api.ComplibException;
055: import org.netbeans.modules.visualweb.palette.api.PaletteItemInfoCookie;
056: import org.openide.filesystems.FileObject;
057: import org.openide.filesystems.FileSystem;
058: import org.openide.filesystems.Repository;
059: import org.openide.loaders.DataFolder;
060: import org.openide.loaders.DataObject;
061: import org.openide.nodes.Node;
062:
063: /**
064: * Facade to simplify interface to underlying NetBeans Palette API
065: *
066: * @author Edwin Goei
067: */
068: class PaletteUtil {
069: /**
070: * Model to represent a palette consisting of three kinds of nodes. Each node can either be a 1)
071: * Palette 2) Category or 3) Item. A Palette can have Category children. A Category can have
072: * Item children. NetBeans does not support an Item with Item children.
073: *
074: * @author Edwin Goei
075: */
076: private static abstract class AbstractPaletteNode {
077: private FileObject fileObject;
078:
079: /**
080: * Call initFileObject() to fully init this class.
081: */
082: private AbstractPaletteNode() {
083: }
084:
085: private AbstractPaletteNode(FileObject fo) {
086: initFileObject(fo);
087: }
088:
089: public void initFileObject(FileObject fileObject) {
090: this .fileObject = fileObject;
091: }
092:
093: public void remove() {
094: /*
095: * Deleting the FileObject itself causes intermittent exceptions so we destroy the Node
096: * instead
097: */
098: try {
099: DataObject dataObject = DataObject.find(fileObject);
100: Node node = dataObject.getNodeDelegate();
101: node.destroy();
102: } catch (IOException e) {
103: IdeUtil.logWarning("Unable to remove FileObject '"
104: + fileObject.getNameExt() + "'", e);
105: }
106: }
107:
108: protected FileObject getFileObject() {
109: return fileObject;
110: }
111:
112: @Override
113: public String toString() {
114: return getFileObject().getPath();
115: }
116: }
117:
118: static class Palette extends AbstractPaletteNode {
119: private static WeakHashMap<FileObject, Category> foMap = new WeakHashMap<FileObject, Category>();
120:
121: private Palette(String path) {
122: FileObject paletteFileObject = Repository.getDefault()
123: .getDefaultFileSystem().findResource(path);
124: initFileObject(paletteFileObject);
125: }
126:
127: public List<Category> getChildren() {
128: ArrayList<Category> result = new ArrayList<Category>();
129: for (FileObject fo : getFileObject().getChildren()) {
130: Category cat = foMap.get(fo);
131: if (cat == null) {
132: cat = new Category(fo);
133: foMap.put(fo, cat);
134: }
135: result.add(cat);
136: }
137: return result;
138: }
139:
140: /**
141: * Get or create a category. If a new category is created, then it also tries to make the
142: * category first, so the user can easily see it. If the category exists, don't change its
143: * location because the user may have intentionally moved it there.
144: *
145: * @param catName
146: * @return
147: * @throws ComplibException
148: */
149: public Category getOrCreateCategory(String catName)
150: throws ComplibException {
151: FileObject categoryFo;
152: try {
153: FileObject paletteFo = getFileObject();
154: FileObject file = paletteFo.getFileObject(catName);
155: if (file == null) {
156: // No category exists yet so create it
157: categoryFo = paletteFo.createFolder(catName);
158:
159: // Tells NB not to issue warning about missing file attribute
160: categoryFo.setAttribute(POSITION, 0);
161:
162: /*
163: * Mark this category as one that was created by the complib module so that it
164: * can be automatically removed later.
165: */
166: categoryFo.setAttribute(CREATED_BY_COMPLIB,
167: Boolean.TRUE);
168:
169: // Try to move new category to the top so user can see it
170: try {
171: makeFileObjectFirst(categoryFo);
172: } catch (IOException e1) {
173: IdeUtil.logWarning(e1);
174: }
175: } else if (!file.isFolder()) {
176: // This should not normally happen
177: // Plain file was found
178: throw new ComplibException(
179: "Unable to create category folder, found plain file: "
180: + file);
181: } else {
182: // Folder was already created
183: categoryFo = file;
184: }
185: } catch (IOException e) {
186: throw new ComplibException("Unable to create category",
187: e);
188: }
189:
190: return new Category(categoryFo);
191: }
192:
193: private void makeFileObjectFirst(FileObject catFo)
194: throws IOException {
195: // Use DataFolder.setOrder() to set the ordering of categories
196: DataFolder palDf = (DataFolder) DataObject
197: .find(getFileObject());
198: DataObject[] childrenArray = palDf.getChildren();
199: List<DataObject> childrenList = Arrays
200: .asList(childrenArray);
201: // Use a LinkedList which supports List.remove()
202: LinkedList<DataObject> children = new LinkedList<DataObject>(
203: childrenList);
204: DataObject catDo = DataObject.find(catFo);
205: children.remove(catDo);
206: children.add(0, catDo);
207: childrenArray = (DataObject[]) children
208: .toArray(new DataObject[children.size()]);
209: palDf.setOrder(childrenArray);
210: }
211: }
212:
213: /**
214: * Represents a category type node in the palette model.
215: *
216: * @author Edwin Goei
217: */
218: static class Category extends AbstractPaletteNode {
219: private static WeakHashMap<FileObject, Item> foMap = new WeakHashMap<FileObject, Item>();
220:
221: private Category(FileObject folder) {
222: super (folder);
223: }
224:
225: public List<Item> getChildren() {
226: ArrayList<Item> result = new ArrayList<Item>();
227: for (FileObject fo : getFileObject().getChildren()) {
228: Item item = foMap.get(fo);
229: if (item == null) {
230: try {
231: item = createItem(fo);
232: } catch (IOException e) {
233: IdeUtil.logWarning(
234: "Skipping child item FileObject", e);
235: continue;
236: }
237: foMap.put(fo, item);
238: }
239: result.add(item);
240: }
241: return result;
242: }
243:
244: /**
245: * Try to create an Item child of this parent from a FileObject and throw an exception if a
246: * problem occurs.
247: *
248: * @param fo
249: * @return
250: * @throws IOException
251: */
252: private Item createItem(FileObject fo) throws IOException {
253: DataObject dataObject = DataObject.find(fo);
254:
255: Item newItem;
256: if (dataObject instanceof ComplibPaletteItemDataObject) {
257: // Complib palette item
258: ComplibPaletteItemDataObject cpido = (ComplibPaletteItemDataObject) dataObject;
259: newItem = new Item(fo, cpido.getClassName(), cpido
260: .getComplib());
261: } else {
262: // Built-in complib palette item currently comes from the rave
263: // palette module
264:
265: // TODO workaround: get the display name to force a file read
266: dataObject.getNodeDelegate().getDisplayName();
267:
268: PaletteItemInfoCookie itemInfo = (PaletteItemInfoCookie) dataObject
269: .getCookie(PaletteItemInfoCookie.class);
270: if (itemInfo == null) {
271: throw new IOException(
272: "PaletteItemInfoCookie is null");
273: }
274: newItem = new Item(fo, itemInfo.getClassName(), null);
275: }
276: return newItem;
277: }
278:
279: /**
280: * Create a child Item using the appropriate component info
281: *
282: * @param compInfo
283: * @throws IOException
284: */
285: public void createItem(ComponentInfo compInfo)
286: throws IOException {
287: final String className = compInfo.getClassName();
288: Complib complib = compInfo.getComplib();
289: final Identifier complibId = complib.getIdentifier();
290:
291: Repository.getDefault().getDefaultFileSystem()
292: .runAtomicAction(new FileSystem.AtomicAction() {
293: public void run() throws IOException {
294: try {
295: ComplibPaletteItemDataObject
296: .createFile(
297: getFileObject(),
298: className,
299: complibId
300: .getNamespaceUriString(),
301: complibId
302: .getVersionString());
303: } catch (IOException e) {
304: IdeUtil
305: .logWarning(
306: "Unable to create complib palette item file",
307: e);
308: throw e;
309: }
310: }
311: });
312: }
313:
314: public String getName() {
315: return getFileObject().getNameExt();
316: }
317:
318: @Override
319: public String toString() {
320: return getName() + "{" + super .toString() + "}";
321: }
322:
323: public boolean isCreatedByComplib() {
324: return PaletteUtil.isCreatedByComplib(getFileObject());
325: }
326: }
327:
328: /**
329: * Represents an item type node in the palette model.
330: *
331: * @author Edwin Goei
332: */
333: static class Item extends AbstractPaletteNode {
334: private static final Identifier INVALID_ID = new Identifier(
335: "urn:invalid-complib-id", "1.0.0");
336:
337: private String className;
338:
339: private Complib complib;
340:
341: private Item(FileObject fo, String className, Complib complib) {
342: super (fo);
343: this .className = className;
344: this .complib = complib;
345: }
346:
347: public String getClassName() {
348: return className;
349: }
350:
351: public Identifier getComplibId() {
352: // TODO Figure out a better way to handle null complib??
353: if (complib == null) {
354: return INVALID_ID;
355: }
356:
357: return complib.getIdentifier();
358: }
359:
360: public Complib getComplib() {
361: return complib;
362: }
363:
364: @Override
365: public String toString() {
366: return getClassName() + "{" + super .toString() + "}";
367: }
368: }
369:
370: // TODO Danger: this name needs to remain in sync with DesignerTopComponent
371: private static final Palette J2EE_1_4 = new Palette(
372: "CreatorDesignerPalette");
373:
374: private static final Palette JAVA_EE_5 = new Palette(
375: "CreatorDesignerPalette5");
376:
377: private static final List<Palette> PALETTES_FOR_J2EE_1_4;
378:
379: private static final List<Palette> PALETTES_FOR_JAVA_EE_5;
380:
381: static {
382: PALETTES_FOR_JAVA_EE_5 = new ArrayList<Palette>(1);
383: PALETTES_FOR_JAVA_EE_5.add(JAVA_EE_5);
384:
385: PALETTES_FOR_J2EE_1_4 = new ArrayList<Palette>(
386: PALETTES_FOR_JAVA_EE_5);
387: PALETTES_FOR_J2EE_1_4.add(J2EE_1_4);
388: }
389:
390: private static final String CREATED_BY_COMPLIB = "created-by-complib";
391:
392: /**
393: * Documented in http://wiki.netbeans.org/wiki/view/FolderOrdering103187 but I don't see where
394: * this constant is defined anywhere so we define it here.
395: */
396: private static final String POSITION = "position";
397:
398: /**
399: * Return a list of palettes to install components from a complib based on the required Java EE
400: * spec version declared in the complib.
401: *
402: * @param complib
403: * @return
404: */
405: public static List<Palette> getPaletteRoots(Complib complib) {
406: return complib.getCompLibManifest().getEeSpecVersion() == EeSpecVersion.J2EE_1_4 ? PALETTES_FOR_J2EE_1_4
407: : PALETTES_FOR_JAVA_EE_5;
408: }
409:
410: /**
411: * Returns a list of all categories from all palettes
412: *
413: * @return
414: */
415: public static List<Category> getAllCategories() {
416: ArrayList<Category> retVal = new ArrayList<Category>();
417: for (Palette pal : PALETTES_FOR_J2EE_1_4) {
418: retVal.addAll(pal.getChildren());
419: }
420: return retVal;
421: }
422:
423: /**
424: * Return true iff the category represented by the FileObject was automatically created by the
425: * complib module as opposed, for example, created by the user.
426: *
427: * @param category
428: * @return
429: */
430: public static boolean isCreatedByComplib(FileObject category) {
431: return category.getAttribute(CREATED_BY_COMPLIB) != null;
432: }
433: }
|