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:
042: package org.netbeans.modules.form.palette;
043:
044: import java.util.*;
045: import java.io.*;
046: import java.beans.*;
047:
048: import org.xml.sax.*;
049: import org.xml.sax.helpers.DefaultHandler;
050:
051: import org.openide.loaders.*;
052: import org.openide.filesystems.*;
053: import org.openide.nodes.*;
054: import org.openide.xml.XMLUtil;
055: import org.openide.actions.*;
056: import org.openide.util.Utilities;
057: import org.openide.util.NbBundle;
058: import org.openide.ErrorManager;
059:
060: import org.netbeans.modules.form.project.ClassSource;
061:
062: /**
063: * DataObject for palette item file. It reads the file and creates PaletteItem
064: * and node from it.
065: *
066: * @author Tomas Pavek
067: */
068:
069: class PaletteItemDataObject extends MultiDataObject {
070:
071: static final String XML_ROOT = "palette_item"; // NOI18N
072: static final String ATTR_VERSION = "version"; // NOI18N
073: static final String TAG_COMPONENT = "component"; // NOI18N
074: static final String ATTR_CLASSNAME = "classname"; // NOI18N
075: static final String ATTR_TYPE = "type"; // NOI18N
076: // static final String ATTR_IS_CONTAINER = "is-container"; // NOI18N
077: static final String TAG_CLASSPATH = "classpath"; // NOI18N
078: static final String TAG_RESOURCE = "resource"; // NOI18N
079: static final String ATTR_NAME = "name"; // NOI18N
080: static final String TAG_DESCRIPTION = "description"; // NOI18N
081: static final String ATTR_BUNDLE = "localizing-bundle"; // NOI18N
082: static final String ATTR_DISPLAY_NAME_KEY = "display-name-key"; // NOI18N
083: static final String ATTR_TOOLTIP_KEY = "tooltip-key"; // NOI18N
084: static final String TAG_ICON16 = "icon16"; // NOI18N
085: static final String ATTR_URL = "urlvalue"; // NOI18N
086: static final String TAG_ICON32 = "icon32"; // NOI18N
087: // component types: "visual", "menu", "layout", "border"
088:
089: private static final Node.PropertySet[] NO_PROPERTIES = new Node.PropertySet[0];
090:
091: private boolean fileLoaded; // at least tried to load
092:
093: private PaletteItem paletteItem;
094:
095: // some raw data read from the file (other passed to PaletteItem)
096: private String displayName_key;
097: private String tooltip_key;
098: private String bundleName;
099: private String icon16URL;
100: private String icon32URL;
101:
102: // resolved data (derived from raw data)
103: String displayName;
104: String tooltip;
105: java.awt.Image icon16;
106: java.awt.Image icon32;
107:
108: // --------
109:
110: PaletteItemDataObject(FileObject fo, MultiFileLoader loader)
111: throws DataObjectExistsException {
112: super (fo, loader);
113: }
114:
115: boolean isFileRead() {
116: return fileLoaded;
117: }
118:
119: boolean isItemValid() {
120: return paletteItem != null;
121: }
122:
123: void reloadFile() {
124: if (paletteItem != null) {
125: paletteItem.reset(); // resets resolved data (but not raw data)
126:
127: paletteItem.componentClassSource = null;
128: // paletteItem.isContainer_explicit = null;
129: paletteItem.componentType_explicit = null;
130: }
131:
132: displayName = null;
133: tooltip = null;
134: icon16 = null;
135: icon32 = null;
136:
137: displayName_key = null;
138: tooltip_key = null;
139: bundleName = null;
140: icon16URL = null;
141: icon32URL = null;
142:
143: loadFile();
144: }
145:
146: // ------
147:
148: @Override
149: public Node createNodeDelegate() {
150: return new ItemNode();
151: }
152:
153: @Override
154: public <T extends Node.Cookie> T getCookie(Class<T> cookieClass) {
155: if (PaletteItem.class.equals(cookieClass)) {
156: if (!fileLoaded)
157: loadFile();
158: return cookieClass.cast(paletteItem);
159: }
160: return super .getCookie(cookieClass);
161: }
162:
163: // -------
164:
165: private void loadFile() {
166: fileLoaded = true;
167: PaletteItem item = paletteItem;
168: if (item == null)
169: item = new PaletteItem(this );
170:
171: FileObject file = getPrimaryFile();
172: if (file.getSize() == 0L) { // item file is empty
173: // just derive the component class name from the file name
174: item.setComponentClassSource(new ClassSource(file.getName()
175: .replace('-', '.')));
176: paletteItem = item;
177: return;
178: }
179:
180: // parse the XML file
181: try {
182: XMLReader reader = XMLUtil.createXMLReader();
183: PaletteItemHandler handler = new PaletteItemHandler();
184: reader.setContentHandler(handler);
185: InputSource input = new InputSource(getPrimaryFile()
186: .getURL().toExternalForm());
187: reader.parse(input);
188: // TODO report errors, validate using DTD?
189:
190: item
191: .setComponentExplicitType(handler.componentExplicitType);
192: if (handler.componentClassName != null
193: || displayName_key != null) {
194: item.setComponentClassSource(new ClassSource(
195: handler.componentClassName, handler.entries));
196: paletteItem = item;
197: }
198: } catch (SAXException saxex) {
199: ErrorManager.getDefault().notify(
200: ErrorManager.INFORMATIONAL, saxex);
201: } catch (IOException ioex) {
202: ErrorManager.getDefault().notify(
203: ErrorManager.INFORMATIONAL, ioex);
204: }
205: }
206:
207: /**
208: * @param folder folder of category where to create new file
209: * @param classname name of the component class
210: * @param source classpath source type - "jar", "library", "project"
211: * @param classpath names of classpath roots - e.g. JAR file paths
212: */
213: static void createFile(FileObject folder, ClassSource classSource)
214: throws IOException {
215: String classname = classSource.getClassName();
216:
217: int idx = classname.lastIndexOf('.');
218: String fileName = FileUtil.findFreeFileName(folder,
219: idx >= 0 ? classname.substring(idx + 1) : classname,
220: PaletteItemDataLoader.ITEM_EXT);
221:
222: FileObject itemFile = folder.createData(fileName,
223: PaletteItemDataLoader.ITEM_EXT);
224:
225: StringBuffer buff = new StringBuffer(512);
226: buff.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n"); // NOI18N
227: buff.append("<palette_item version=\"1.0\">\n"); // NOI18N
228: buff.append(" <component classname=\""); // NOI18N
229: buff.append(classname);
230: buff.append("\" />\n"); // NOI18N
231: buff.append(" <classpath>\n"); // NOI18N
232: for (ClassSource.Entry entry : classSource.getEntries()) {
233: buff.append(" <resource type=\""); // NOI18N
234: buff.append(entry.getPicklingType());
235: buff.append("\" name=\""); // NOI18N
236: buff.append(entry.getPicklingName());
237: buff.append("\" />\n"); // NOI18N
238: buff.append(" </classpath>\n"); // NOI18N
239: buff.append("</palette_item>\n"); // NOI18N
240: }
241:
242: FileLock lock = itemFile.lock();
243: OutputStream os = itemFile.getOutputStream(lock);
244: try {
245: os.write(buff.toString().getBytes("UTF-8")); // NOI18N
246: } finally {
247: os.close();
248: lock.releaseLock();
249: }
250: }
251:
252: // -------
253:
254: /** DataLoader for the palette item files. */
255: public static final class PaletteItemDataLoader extends
256: UniFileLoader {
257:
258: static final String ITEM_EXT = "palette_item"; // NOI18N
259:
260: PaletteItemDataLoader() {
261: super (
262: "org.netbeans.modules.form.palette.PaletteItemDataObject"); // NOI18N
263:
264: ExtensionList ext = new ExtensionList();
265: ext.addExtension(ITEM_EXT);
266: setExtensions(ext);
267: }
268:
269: /** Gets default display name. Overides superclass method. */
270: @Override
271: protected String defaultDisplayName() {
272: return NbBundle.getBundle(PaletteItemDataObject.class)
273: .getString("PROP_PaletteItemLoader_Name"); // NOI18N
274: }
275:
276: protected MultiDataObject createMultiObject(
277: FileObject primaryFile)
278: throws DataObjectExistsException, IOException {
279: return new PaletteItemDataObject(primaryFile, this );
280: }
281: }
282:
283: public static final class PaletteItemDataLoaderBeanInfo extends
284: SimpleBeanInfo {
285: private static String iconURL = "org/netbeans/modules/form/resources/palette_manager.png"; // NOI18N
286:
287: @Override
288: public BeanInfo[] getAdditionalBeanInfo() {
289: try {
290: return new BeanInfo[] { Introspector
291: .getBeanInfo(UniFileLoader.class) };
292: } catch (IntrospectionException ie) {
293: org.openide.ErrorManager.getDefault().notify(ie);
294: return null;
295: }
296: }
297:
298: @Override
299: public java.awt.Image getIcon(final int type) {
300: return Utilities.loadImage(iconURL);
301: }
302:
303: }
304:
305: // --------
306:
307: /** Node representing the palette item (node delegate for the DataObject). */
308: class ItemNode extends DataNode {
309:
310: ItemNode() {
311: super (PaletteItemDataObject.this , Children.LEAF);
312: }
313:
314: @Override
315: public String getDisplayName() {
316: if (!fileLoaded)
317: loadFile();
318:
319: if (displayName == null) {
320: displayName = getExplicitDisplayName();
321: if (displayName == null) { // no explicit name
322: if (isItemValid()) {
323: displayName = paletteItem.getDisplayName();
324: if (displayName == null) { // no name from BeanDescriptor
325: String classname = paletteItem
326: .getComponentClassName();
327: if (classname != null) {
328: int i = classname.lastIndexOf('.'); // NOI18N
329: displayName = i >= 0 ? classname
330: .substring(i + 1) : classname;
331: }
332: }
333: }
334: if (displayName == null) // no name derived from the item
335: displayName = super .getDisplayName();
336: }
337: }
338: return displayName;
339: }
340:
341: @Override
342: public String getShortDescription() {
343: if (!fileLoaded)
344: loadFile();
345:
346: if (tooltip == null) {
347: tooltip = getExplicitTooltip();
348: if (tooltip == null) { // no explicit tooltip
349: if (isItemValid()) {
350: tooltip = paletteItem.getTooltip();
351: if (tooltip == null) // no tooltip from BeanDescriptor
352: tooltip = paletteItem
353: .getComponentClassName();
354: }
355: if (tooltip == null) // no tooltip derived from the item
356: tooltip = getDisplayName();
357: }
358: }
359: return tooltip;
360: }
361:
362: @Override
363: public boolean canRename() {
364: return false;
365: }
366:
367: @Override
368: public java.awt.Image getIcon(int type) {
369: if (!fileLoaded)
370: loadFile();
371:
372: if (type == BeanInfo.ICON_COLOR_32x32
373: || type == BeanInfo.ICON_MONO_32x32) {
374: if (icon32 == null) {
375: icon32 = getExplicitIcon(type);
376: if (icon32 == null && isItemValid())
377: icon32 = paletteItem.getIcon(type);
378: if (icon32 == null)
379: icon32 = Utilities
380: .loadImage("org/netbeans/modules/form/resources/palette/unknown32.gif"); // NOI18N
381: }
382: return icon32;
383: } else { // small icon by default
384: if (icon16 == null) {
385: icon16 = getExplicitIcon(type);
386: if (icon16 == null && isItemValid())
387: icon16 = paletteItem.getIcon(type);
388: if (icon16 == null)
389: icon16 = Utilities
390: .loadImage("org/netbeans/modules/form/resources/palette/unknown.gif"); // NOI18N
391: }
392: return icon16;
393: }
394: // TODO badged icon for invalid item?
395: }
396:
397: // TODO properties
398: @Override
399: public Node.PropertySet[] getPropertySets() {
400: return NO_PROPERTIES;
401: }
402:
403: // ------
404:
405: private String getExplicitDisplayName() {
406: String displayName = null;
407: if (displayName_key != null) {
408: if (bundleName != null) {
409: try {
410: displayName = NbBundle.getBundle(bundleName)
411: .getString(displayName_key);
412: } catch (Exception ex) {
413: } // ignore failure
414: }
415: if (displayName == null)
416: displayName = displayName_key;
417: }
418: return displayName;
419: }
420:
421: private String getExplicitTooltip() {
422: String tooltip = null;
423: if (tooltip_key != null) {
424: if (bundleName != null) {
425: try {
426: tooltip = NbBundle.getBundle(bundleName)
427: .getString(tooltip_key);
428: } catch (Exception ex) {
429: } // ignore failure
430: }
431: if (tooltip == null)
432: tooltip = tooltip_key;
433: }
434: return tooltip;
435: }
436:
437: private java.awt.Image getExplicitIcon(int type) {
438: if (type == BeanInfo.ICON_COLOR_32x32
439: || type == BeanInfo.ICON_MONO_32x32) {
440: if (icon32URL != null) { // explicit icon specified in file
441: try {
442: return java.awt.Toolkit.getDefaultToolkit()
443: .getImage(new java.net.URL(icon32URL));
444: } catch (java.net.MalformedURLException ex) {
445: } // ignore
446: } else if (getPrimaryFile().getAttribute(
447: "SystemFileSystem.icon32") != null) // NOI18N
448: return super .getIcon(type);
449: } else { // get small icon in other cases
450: if (icon16URL != null) { // explicit icon specified in file
451: try {
452: return java.awt.Toolkit.getDefaultToolkit()
453: .getImage(new java.net.URL(icon16URL));
454: } catch (java.net.MalformedURLException ex) {
455: } // ignore
456: } else if (getPrimaryFile().getAttribute(
457: "SystemFileSystem.icon") != null) // NOI18N
458: return super .getIcon(type);
459: }
460: return null;
461: }
462: }
463:
464: private class PaletteItemHandler extends DefaultHandler {
465: List<ClassSource.Entry> entries;
466: String componentClassName;
467: String componentExplicitType;
468:
469: @Override
470: public void startDocument() throws SAXException {
471: entries = new ArrayList<ClassSource.Entry>();
472: componentClassName = null;
473: componentExplicitType = null;
474: }
475:
476: @Override
477: public void startElement(String uri, String localName,
478: String qName, Attributes attributes)
479: throws SAXException {
480: if (XML_ROOT.equals(qName)) {
481: String version = attributes.getValue(ATTR_VERSION);
482: if (version == null) {
483: String message = NbBundle.getBundle(
484: PaletteItemDataObject.class).getString(
485: "MSG_UnknownPaletteItemVersion"); // NOI18N
486: throw new SAXException(message);
487: } else if (!version.startsWith("1.")) { // NOI18N
488: String message = NbBundle.getBundle(
489: PaletteItemDataObject.class).getString(
490: "MSG_UnsupportedPaletteItemVersion"); // NOI18N
491: throw new SAXException(message);
492: }
493: // TODO item ID (for now we take the class name as the ID)
494: } else if (TAG_COMPONENT.equals(qName)) {
495: String className = attributes.getValue(ATTR_CLASSNAME);
496: componentClassName = className;
497: componentExplicitType = attributes.getValue(ATTR_TYPE);
498: } else if (TAG_CLASSPATH.equals(qName)) {
499: // Content is processed in the next branch
500: } else if (TAG_RESOURCE.equals(qName)) {
501: String type = attributes.getValue(ATTR_TYPE);
502: String name = attributes.getValue(ATTR_NAME);
503: if ((type != null) && (name != null)) {
504: ClassSource.Entry entry = ClassSource.unpickle(
505: type, name);
506: if (entry != null) {
507: entries.add(entry);
508: }
509: }
510: } else if (TAG_DESCRIPTION.equals(qName)) {
511: String bundle = attributes.getValue(ATTR_BUNDLE);
512: if (bundle != null) {
513: PaletteItemDataObject.this .bundleName = bundle;
514: }
515: String displayNameKey = attributes
516: .getValue(ATTR_DISPLAY_NAME_KEY);
517: if (displayNameKey != null) {
518: PaletteItemDataObject.this .displayName_key = displayNameKey;
519: }
520: String tooltipKey = attributes
521: .getValue(ATTR_TOOLTIP_KEY);
522: if (tooltipKey != null) {
523: PaletteItemDataObject.this .tooltip_key = tooltipKey;
524: }
525: } else if (TAG_ICON16.equals(qName)) {
526: String url = attributes.getValue(ATTR_URL);
527: if (url != null) {
528: PaletteItemDataObject.this .icon16URL = url;
529: }
530: // TODO support also class resource name for icons
531: } else if (TAG_ICON32.equals(qName)) {
532: String url = attributes.getValue(ATTR_URL);
533: if (url != null) {
534: PaletteItemDataObject.this .icon32URL = url;
535: }
536: // TODO support also class resource name for icons
537: }
538: }
539: }
540:
541: }
|