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.awt.Image;
045: import java.awt.datatransfer.DataFlavor;
046: import java.awt.datatransfer.Transferable;
047: import java.awt.datatransfer.UnsupportedFlavorException;
048: import java.awt.dnd.DnDConstants;
049: import java.beans.BeanDescriptor;
050: import java.beans.BeanInfo;
051: import java.beans.IntrospectionException;
052: import java.beans.Introspector;
053: import java.beans.SimpleBeanInfo;
054: import java.io.File;
055: import java.io.IOException;
056: import java.lang.reflect.InvocationTargetException;
057: import java.lang.reflect.Method;
058:
059: import org.openide.ErrorManager;
060: import org.openide.filesystems.FileObject;
061: import org.openide.filesystems.FileUtil;
062: import org.openide.loaders.DataNode;
063: import org.openide.loaders.DataObjectExistsException;
064: import org.openide.loaders.ExtensionList;
065: import org.openide.loaders.MultiDataObject;
066: import org.openide.loaders.MultiFileLoader;
067: import org.openide.loaders.UniFileLoader;
068: import org.openide.nodes.Children;
069: import org.openide.nodes.Node;
070: import org.openide.util.Lookup;
071: import org.openide.util.NbBundle;
072: import org.openide.util.Utilities;
073: import org.openide.util.datatransfer.ExTransferable;
074: import org.openide.xml.XMLUtil;
075: import org.w3c.dom.Document;
076: import org.w3c.dom.Element;
077: import org.xml.sax.Attributes;
078: import org.xml.sax.InputSource;
079: import org.xml.sax.SAXException;
080: import org.xml.sax.XMLReader;
081: import org.xml.sax.helpers.DefaultHandler;
082:
083: import com.sun.rave.designtime.DisplayItem;
084: import com.sun.rave.designtime.impl.BasicBeanCreateInfo;
085:
086: /**
087: * DataObject for palette item file. It reads the file and creates PaletteItem
088: * and node from it.
089: *
090: * @author Tomas Pavek
091: * @author Edwin Goei
092: */
093:
094: class ComplibPaletteItemDataObject extends MultiDataObject {
095: private static final String ELM_COMPLIB_ITEM = "complibItem";
096:
097: private static final String ATTR_VERSION = "version"; // NOI18N
098:
099: private static final String ELM_COMPONENT = "component"; // NOI18N
100:
101: private static final String ATTR_CLASSNAME = "className"; // NOI18N
102:
103: private static final String ATTR_COMPLIB_VERSION = "complibVersion";
104:
105: private static final String ATTR_COMPLIB_NAMESPACE = "complibNamespace";
106:
107: private static final Node.PropertySet[] NO_PROPERTIES = new Node.PropertySet[0];
108:
109: private boolean fileLoaded; // at least tried to load
110:
111: private BeanInfo beanInfo;
112:
113: private String className;
114:
115: private Complib complib;
116:
117: ComplibPaletteItemDataObject(FileObject fo, MultiFileLoader loader)
118: throws IOException {
119: super (fo, loader);
120: }
121:
122: public Node createNodeDelegate() {
123: return new ItemNode();
124: }
125:
126: /**
127: * Returns the complib associated with this palette item (normal case) or
128: * null if one cannot be found. For example, it is possible for someone to
129: * muck with their userdir such that they remove a complib, but yet the
130: * palette data still exists and now refers to a non-existant complib. In
131: * this case, null will be returned here.
132: *
133: * @return associated complib or null
134: */
135: public Complib getComplib() {
136: loadFile();
137: return complib;
138: }
139:
140: private BeanInfo getComponentBeanInfo() {
141: if (beanInfo == null) {
142: Complib aComplib = getComplib();
143: if (aComplib == null) {
144: // Complib is missing so try to recover
145: beanInfo = getFallbackBeanInfo();
146: } else {
147: try {
148: // Normal case
149: beanInfo = aComplib.getBeanInfo(className);
150: } catch (Exception e) {
151: // Unable to load BeanInfo so try to recover
152: IdeUtil.logError(e);
153: beanInfo = getFallbackBeanInfo();
154: }
155: }
156: }
157: return beanInfo;
158: }
159:
160: private BeanInfo getFallbackBeanInfo() {
161: beanInfo = UnknownBeanInfo.getInstance();
162:
163: // Try to recover by removing the palette item file object
164: FileObject primaryFile = getPrimaryFile();
165: try {
166: primaryFile.delete();
167: } catch (IOException e1) {
168: IdeUtil.logWarning("Unable to remove FileObject '"
169: + primaryFile.getNameExt() + "'", e1);
170: }
171:
172: return beanInfo;
173: }
174:
175: private void loadFile() {
176: if (fileLoaded) {
177: return;
178: }
179:
180: // Synchronized to prevent partial file reads. See createFile().
181: synchronized (getClass()) {
182: PaletteItemHandler handler = null;
183:
184: // parse the XML file
185: try {
186: XMLReader reader = XMLUtil.createXMLReader();
187: handler = new PaletteItemHandler();
188: reader.setContentHandler(handler);
189:
190: InputSource input = new InputSource(getPrimaryFile()
191: .getURL().toExternalForm());
192: reader.parse(input);
193: // TODO report errors, validate using DTD?
194: } catch (SAXException saxex) {
195: ErrorManager.getDefault().notify(
196: ErrorManager.INFORMATIONAL, saxex);
197: } catch (IOException ioex) {
198: ErrorManager.getDefault().notify(
199: ErrorManager.INFORMATIONAL, ioex);
200: }
201:
202: className = handler.getClassName();
203: String namespace = handler.getNamespace();
204: String version = handler.getVersion();
205:
206: // Try to find the installed complib
207: complib = ComplibServiceProvider.getInstance()
208: .getInstalledComplib(namespace, version);
209:
210: fileLoaded = true;
211: }
212: }
213:
214: /**
215: * Returns the class name associated with this palette item.
216: *
217: * @return class name of palette item
218: */
219: public String getClassName() {
220: loadFile();
221: return className;
222: }
223:
224: /**
225: * Create a file corresponding to a palette item. This method is
226: * synchronized so that other threads that read the file via loadFile() will
227: * not get partial file contents.
228: *
229: * @param parent
230: * @param className
231: * @param namespace
232: * @param version
233: * @return
234: * @throws IOException
235: */
236: static synchronized FileObject createFile(FileObject parent,
237: String className, String namespace, String version)
238: throws IOException {
239: String fileExtension = PaletteItemDataLoader.ITEM_EXT;
240: String baseFileName = FileUtil.findFreeFileName(parent, IdeUtil
241: .baseClassName(className), fileExtension);
242:
243: XmlUtil xmlOut = new XmlUtil();
244: Document doc = xmlOut.createDocument();
245:
246: Element rootElm = doc.createElement(ELM_COMPLIB_ITEM);
247: rootElm.setAttribute(ATTR_VERSION, "1.0");
248: doc.appendChild(rootElm);
249:
250: Element compElm = doc.createElement(ELM_COMPONENT);
251: compElm.setAttribute(ATTR_CLASSNAME, className);
252: compElm.setAttribute(ATTR_COMPLIB_NAMESPACE, namespace);
253: compElm.setAttribute(ATTR_COMPLIB_VERSION, version);
254: rootElm.appendChild(compElm);
255:
256: FileObject itemFO = parent.createData(baseFileName,
257: fileExtension);
258: File itemFile = FileUtil.toFile(itemFO);
259: xmlOut.write(itemFile);
260: return itemFO;
261: }
262:
263: /** DataLoader for the palette item files. */
264: public static final class PaletteItemDataLoader extends
265: UniFileLoader {
266:
267: static final String ITEM_EXT = "complib_item"; // NOI18N
268:
269: PaletteItemDataLoader() {
270: super (ComplibPaletteItemDataObject.class.getName());
271:
272: ExtensionList ext = new ExtensionList();
273: ext.addExtension(ITEM_EXT);
274: setExtensions(ext);
275: }
276:
277: /** Gets default display name. Overides superclass method. */
278: protected String defaultDisplayName() {
279: return NbBundle.getBundle(
280: ComplibPaletteItemDataObject.class).getString(
281: "PROP_PaletteItemLoader_Name"); // NOI18N
282: }
283:
284: protected MultiDataObject createMultiObject(
285: FileObject primaryFile)
286: throws DataObjectExistsException, IOException {
287: return new ComplibPaletteItemDataObject(primaryFile, this );
288: }
289: }
290:
291: public static final class PaletteItemDataLoaderBeanInfo extends
292: SimpleBeanInfo {
293: private static String iconURL = "org/netbeans/modules/form/resources/palette_manager.png"; // NOI18N
294:
295: public BeanInfo[] getAdditionalBeanInfo() {
296: try {
297: return new BeanInfo[] { Introspector
298: .getBeanInfo(UniFileLoader.class) };
299: } catch (IntrospectionException ie) {
300: org.openide.ErrorManager.getDefault().notify(ie);
301: return null;
302: }
303: }
304:
305: public java.awt.Image getIcon(final int type) {
306: return Utilities.loadImage(iconURL);
307: }
308:
309: }
310:
311: // --------
312:
313: /** Node representing the palette item (node delegate for the DataObject). */
314: class ItemNode extends DataNode {
315: ItemNode() {
316: super (ComplibPaletteItemDataObject.this , Children.LEAF);
317: }
318:
319: public String getDisplayName() {
320: BeanInfo componentInfo = getComponentBeanInfo();
321: return componentInfo == null ? "Unknown" : componentInfo
322: .getBeanDescriptor().getDisplayName();
323: }
324:
325: public String getShortDescription() {
326: BeanInfo componentInfo = getComponentBeanInfo();
327: return componentInfo == null ? "Unknown" : componentInfo
328: .getBeanDescriptor().getShortDescription();
329: }
330:
331: public boolean canRename() {
332: return false;
333: }
334:
335: public Image getIcon(int type) {
336: BeanInfo componentInfo = getComponentBeanInfo();
337: Image icon = componentInfo == null ? null : componentInfo
338: .getIcon(type);
339: if (icon == null) {
340: icon = Utilities
341: .loadImage("org/netbeans/modules/visualweb/palette/resources/custom_component.png"); // NOI18N
342: }
343: return icon;
344: }
345:
346: // TODO properties
347: public Node.PropertySet[] getPropertySets() {
348: return NO_PROPERTIES;
349: }
350:
351: public Transferable clipboardCopy() throws IOException {
352: // ensureComplibCopiedToProject();
353: Transferable t = super .clipboardCopy();
354: addPaletteFlavor(t);
355: return t;
356: }
357:
358: public Transferable clipboardCut() throws IOException {
359: Transferable t = super .clipboardCut();
360: addPaletteFlavor(t);
361: return t;
362: }
363:
364: public Transferable drag() throws IOException {
365: // ensureComplibCopiedToProject();
366: Transferable t = super .drag();
367: addPaletteFlavor(t);
368:
369: // XXX NB#82645, when the issue is fixed, remove this.
370: hackExplorerDnD(this , t);
371:
372: return t;
373: }
374:
375: private Transferable addPaletteFlavor(Transferable t) {
376: // ExTransferable allows you to add additional flavors.
377: ExTransferable et = ExTransferable.create(t);
378:
379: // The DisplayItem Flavor make items dropable.
380: String humanName = NbBundle.getMessage(
381: ComplibPaletteItemDataObject.class, "humanName");
382: DataFlavor df = new DataFlavor(
383: DataFlavor.javaJVMLocalObjectMimeType + "; class="
384: + DisplayItem.class.getName(), humanName);
385:
386: ExTransferable.Single ex_sgl = new ExTransferable.Single(df) {
387: protected Object getData() throws IOException,
388: UnsupportedFlavorException {
389: return new BasicBeanCreateInfo(getClassName(),
390: getDisplayName());
391: }
392:
393: };
394: et.put(ex_sgl);
395:
396: return t;
397: }
398: }
399:
400: private class PaletteItemHandler extends DefaultHandler {
401: private String className;
402:
403: private String namespace;
404:
405: private String version;
406:
407: public void startElement(String uri, String localName,
408: String qName, Attributes attributes)
409: throws SAXException {
410: if (ELM_COMPLIB_ITEM.equals(qName)) {
411: String version = attributes.getValue(ATTR_VERSION);
412: if (version == null) {
413: String message = NbBundle.getBundle(
414: ComplibPaletteItemDataObject.class)
415: .getString("MSG_UnknownPaletteItemVersion"); // NOI18N
416: throw new SAXException(message);
417: } else if (!version.startsWith("1.")) { // NOI18N
418: String message = NbBundle
419: .getBundle(
420: ComplibPaletteItemDataObject.class)
421: .getString(
422: "MSG_UnsupportedPaletteItemVersion"); // NOI18N
423: throw new SAXException(message);
424: }
425: } else if (ELM_COMPONENT.equals(qName)) {
426: className = attributes.getValue(ATTR_CLASSNAME);
427: namespace = attributes.getValue(ATTR_COMPLIB_NAMESPACE);
428: version = attributes.getValue(ATTR_COMPLIB_VERSION);
429: }
430: }
431:
432: public String getClassName() {
433: return className;
434: }
435:
436: public String getNamespace() {
437: return namespace;
438: }
439:
440: public String getVersion() {
441: return version;
442: }
443: }
444:
445: private static class UnknownBeanInfo extends SimpleBeanInfo {
446: private BeanDescriptor beanDescriptor;
447:
448: private static final UnknownBeanInfo INSTANCE = new UnknownBeanInfo();
449:
450: public static UnknownBeanInfo getInstance() {
451: return INSTANCE;
452: }
453:
454: public BeanDescriptor getBeanDescriptor() {
455: if (beanDescriptor == null) {
456: beanDescriptor = new UnknownBeanDescriptor();
457: }
458: return beanDescriptor;
459: }
460:
461: public Image getIcon(int iconKind) {
462: return Utilities
463: .loadImage("org/netbeans/modules/visualweb/palette/resources/custom_component.png"); // NOI18N
464: }
465: }
466:
467: private static class UnknownBeanDescriptor extends BeanDescriptor {
468: public UnknownBeanDescriptor() {
469: super (Object.class);
470: }
471:
472: public String getDisplayName() {
473: return "Unknown";
474: }
475:
476: public String getShortDescription() {
477: return "Unknown short description";
478: };
479: }
480:
481: // ////////////////////
482: // >>> Hack workaround.
483: /** XXX Workaround of NB #82645. */
484: private static void hackExplorerDnD(ItemNode itemNode,
485: Transferable trans) {
486: Object explorerDnDManager = getExplorerDnDManager();
487: if (explorerDnDManager != null) {
488: setNodeAllowedActions(explorerDnDManager,
489: DnDConstants.ACTION_MOVE);
490: setDraggedTransferable(explorerDnDManager, trans, true);
491: setDraggedNodes(explorerDnDManager, new Node[] { itemNode });
492: setDnDActive(explorerDnDManager, true);
493: }
494: }
495:
496: private static void setNodeAllowedActions(
497: Object explorerDnDManager, int actions) {
498: invokeOnExplorerDnDManager(explorerDnDManager,
499: "setNodeAllowedActions", // NOI18N
500: new Class[] { Integer.TYPE }, new Object[] { Integer
501: .valueOf(actions) });
502: }
503:
504: private static void setDraggedTransferable(
505: Object explorerDnDManager, Transferable trans, boolean isCut) {
506: invokeOnExplorerDnDManager(
507: explorerDnDManager,
508: "setDraggedTransferable", // NOI18N
509: new Class[] { Transferable.class, Boolean.TYPE },
510: new Object[] { trans, Boolean.valueOf(isCut) });
511: }
512:
513: private static void setDraggedNodes(Object explorerDnDManager,
514: Node[] nodes) {
515: invokeOnExplorerDnDManager(explorerDnDManager,
516: "setDraggedNodes", // NOI18N
517: new Class[] { Node[].class }, new Object[] { nodes });
518: }
519:
520: private static void setDnDActive(Object explorerDnDManager,
521: boolean active) {
522: invokeOnExplorerDnDManager(explorerDnDManager, "setDnDActive", // NOI18N
523: new Class[] { Boolean.TYPE }, new Object[] { Boolean
524: .valueOf(active) });
525: }
526:
527: private static Object getExplorerDnDManager() {
528: ClassLoader contextCL = (ClassLoader) Lookup.getDefault()
529: .lookup(ClassLoader.class);
530: try {
531: Class explorerDnDClass = Class.forName(
532: "org.openide.explorer.view.ExplorerDnDManager",
533: true, contextCL); // NOI18N
534: Method getDefaultMethod = explorerDnDClass
535: .getDeclaredMethod("getDefault", new Class[0]); // NOI18N
536: getDefaultMethod.setAccessible(true);
537: return getDefaultMethod.invoke(null, new Object[0]);
538: } catch (IllegalArgumentException ex) {
539: ErrorManager.getDefault().notify(
540: ErrorManager.INFORMATIONAL, ex);
541: } catch (InvocationTargetException ex) {
542: ErrorManager.getDefault().notify(
543: ErrorManager.INFORMATIONAL, ex);
544: } catch (IllegalAccessException ex) {
545: ErrorManager.getDefault().notify(
546: ErrorManager.INFORMATIONAL, ex);
547: } catch (SecurityException ex) {
548: ErrorManager.getDefault().notify(
549: ErrorManager.INFORMATIONAL, ex);
550: } catch (NoSuchMethodException ex) {
551: ErrorManager.getDefault().notify(
552: ErrorManager.INFORMATIONAL, ex);
553: } catch (ClassNotFoundException ex) {
554: ErrorManager.getDefault().notify(
555: ErrorManager.INFORMATIONAL, ex);
556: }
557: return null;
558: }
559:
560: private static void invokeOnExplorerDnDManager(
561: Object explorerDnDManager, String methodName,
562: Class[] argumentTypes, Object[] arguments) {
563: try {
564: Method setNodeAllowedActions = explorerDnDManager
565: .getClass().getDeclaredMethod(methodName,
566: argumentTypes);
567: setNodeAllowedActions.setAccessible(true);
568: setNodeAllowedActions.invoke(explorerDnDManager, arguments);
569: } catch (IllegalArgumentException ex) {
570: ErrorManager.getDefault().notify(
571: ErrorManager.INFORMATIONAL, ex);
572: } catch (IllegalAccessException ex) {
573: ErrorManager.getDefault().notify(
574: ErrorManager.INFORMATIONAL, ex);
575: } catch (InvocationTargetException ex) {
576: ErrorManager.getDefault().notify(
577: ErrorManager.INFORMATIONAL, ex);
578: } catch (SecurityException ex) {
579: ErrorManager.getDefault().notify(
580: ErrorManager.INFORMATIONAL, ex);
581: } catch (NoSuchMethodException ex) {
582: ErrorManager.getDefault().notify(
583: ErrorManager.INFORMATIONAL, ex);
584: }
585: }
586: // <<< Hack workaround.
587: // ////////////////////
588: }
|