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.vmd.palette;
043:
044: import org.netbeans.modules.vmd.api.model.ComponentProducer;
045: import org.netbeans.modules.vmd.api.model.ComponentSerializationSupport;
046: import org.netbeans.modules.vmd.api.model.Debug;
047: import org.netbeans.modules.vmd.api.model.DescriptorRegistry;
048: import org.netbeans.modules.vmd.api.model.DesignDocument;
049: import org.netbeans.modules.vmd.api.model.common.DefaultDataFlavor;
050: import org.netbeans.modules.vmd.api.palette.PaletteProvider;
051: import org.netbeans.spi.palette.*;
052: import org.openide.filesystems.*;
053: import org.openide.filesystems.FileSystem.AtomicAction;
054: import org.openide.loaders.DataFolder;
055: import org.openide.util.Lookup;
056: import org.openide.util.LookupEvent;
057: import org.openide.util.RequestProcessor;
058: import org.openide.util.datatransfer.ExTransferable;
059: import javax.swing.*;
060: import java.awt.event.ActionEvent;
061: import java.io.IOException;
062: import java.io.OutputStream;
063: import java.lang.ref.WeakReference;
064: import java.util.*;
065: import java.util.concurrent.atomic.AtomicBoolean;
066: import org.openide.util.Lookup.Result;
067: import org.openide.util.LookupListener;
068:
069: /**
070: * @author David Kaspar, Anton Chechel
071: */
072: public class PaletteKit implements Runnable, LookupListener {
073:
074: static final String CUSTOM_CATEGORY_NAME = "custom"; // NOI18N
075: private static final String PALETTE_FOLDER_NAME = "palette"; // NOI18N
076:
077: private WeakReference<DesignDocument> activeDocument;
078: private PaletteController paletteController;
079: private DNDHandler dndHandler;
080: private Map<String, PaletteItemDataNode> nodesMap;
081: private boolean isValidationRunning;
082: private LinkedList<Lookup> validationQueue;
083: private DataFolder rootFolder;
084: private FileSystem fs;
085: private Result<PaletteProvider> lookupResult;
086: private final AtomicBoolean requiresPaletteInit = new AtomicBoolean(
087: false);
088:
089: PaletteKit(final String projectType) {
090: this .fs = Repository.getDefault().getDefaultFileSystem();
091:
092: validationQueue = new LinkedList<Lookup>();
093: lookupResult = Lookup.getDefault().lookupResult(
094: PaletteProvider.class);
095: lookupResult.addLookupListener(this );
096:
097: String rootFolderPath = projectType + '/' + PALETTE_FOLDER_NAME; // NOI18N
098: nodesMap = new HashMap<String, PaletteItemDataNode>();
099: try {
100: FileObject rootFolderFO = fs.findResource(rootFolderPath);
101: if (rootFolderFO == null) {
102: FileObject projectTypeFO = fs.findResource(projectType);
103: if (projectTypeFO == null) {
104: projectTypeFO = fs.getRoot().createFolder(
105: projectType);
106: }
107: rootFolderFO = FileUtil.createFolder(projectTypeFO,
108: PALETTE_FOLDER_NAME);
109: }
110:
111: rootFolder = DataFolder.findFolder(rootFolderFO);
112: rootFolder.getPrimaryFile()
113: .setAttribute("itemWidth", "120"); // NOI18N
114: dndHandler = new DNDHandler();
115: paletteController = PaletteFactory.createPalette(
116: rootFolderPath, new Actions(), new Filter(),
117: dndHandler);
118: } catch (IOException ex) {
119: throw Debug.error(ex);
120: }
121: }
122:
123: synchronized void clean() {
124: if (activeDocument == null || activeDocument.get() == null) {
125: return;
126: }
127:
128: String projectID = activeDocument.get().getDocumentInterface()
129: .getProjectID();
130: String projectType = activeDocument.get()
131: .getDocumentInterface().getProjectType();
132:
133: final DescriptorRegistry registry = DescriptorRegistry
134: .getDescriptorRegistry(projectType, projectID);
135: registry.readAccess(new Runnable() {
136:
137: public void run() {
138: List<ComponentProducer> list = registry
139: .getComponentProducers();
140: Map<String, ComponentProducer> producers = new HashMap<String, ComponentProducer>(
141: list.size());
142: for (ComponentProducer producer : list) {
143: producers.put(producer.getProducerID(), producer);
144: }
145: cleanCore(producers);
146: }
147: });
148: }
149:
150: private void cleanCore(Map<String, ComponentProducer> producers) {
151: try {
152: for (FileObject catFolder : rootFolder.getPrimaryFile()
153: .getChildren()) {
154: for (FileObject item : catFolder.getChildren()) {
155: item.delete();
156: }
157: }
158: } catch (IOException ex) {
159: Debug.warning(ex);
160: }
161: }
162:
163: synchronized void refresh() {
164: refreshFO(rootFolder.getPrimaryFile());
165: }
166:
167: private void refreshFO(FileObject fo) {
168: if (fo.isFolder()) {
169: for (FileObject fileObject : fo.getChildren()) {
170: refreshFO(fileObject);
171: }
172: } else {
173: fo.refresh();
174: }
175: }
176:
177: synchronized void refreshDescriptorRegistry() {
178: if (activeDocument == null || activeDocument.get() == null) {
179: return;
180: }
181:
182: String projectType = activeDocument.get()
183: .getDocumentInterface().getProjectType();
184: ComponentSerializationSupport
185: .refreshDescriptorRegistry(projectType);
186: }
187:
188: public PaletteController getPaletteController() {
189: return paletteController;
190: }
191:
192: public DragAndDropHandler getDndHandler() {
193: return dndHandler;
194: }
195:
196: void refreshPaletteController() {
197: if (paletteController == null) {
198: return;
199: }
200: paletteController.refresh();
201: }
202:
203: synchronized void init() {
204: if (activeDocument == null || activeDocument.get() == null) {
205: return;
206: }
207:
208: if (requiresPaletteInit.getAndSet(true)) {
209: return;
210: }
211:
212: final String projectID = activeDocument.get()
213: .getDocumentInterface().getProjectID();
214: final String projectType = activeDocument.get()
215: .getDocumentInterface().getProjectType();
216:
217: final DescriptorRegistry registry = DescriptorRegistry
218: .getDescriptorRegistry(projectType, projectID);
219: registry.readAccess(new Runnable() {
220:
221: public void run() {
222: while (requiresPaletteInit.getAndSet(false)) {
223: Collection<? extends PaletteProvider> providers = lookupResult
224: .allInstances();
225: for (PaletteProvider provider : providers) {
226: if (provider != null) {
227: provider.initPaletteCategories(projectType);
228: }
229: }
230: initCore(registry.getComponentProducers());
231: }
232: }
233: });
234: }
235:
236: private void initCore(final List<ComponentProducer> producers) {
237: FileObject[] children = rootFolder.getPrimaryFile()
238: .getChildren();
239: Map<String, FileObject> categoryFolders = new HashMap<String, FileObject>(
240: children.length);
241: for (FileObject fo : children) {
242: categoryFolders.put(fo.getName(), fo);
243: }
244:
245: // create item files
246: for (ComponentProducer producer : producers) {
247: if (producer.getPaletteDescriptor() == null) {
248: continue;
249: }
250:
251: String producerID = producer.getProducerID();
252: String catID = producer.getPaletteDescriptor()
253: .getCategoryID();
254: FileObject catFO;
255: if (catID != null) {
256: catFO = categoryFolders.get(catID);
257: } else {
258: catFO = categoryFolders.get(CUSTOM_CATEGORY_NAME);
259: if (catFO == null) {
260: continue;
261: }
262: }
263:
264: if (catFO == null) {
265: // if category folder was not initialized - create folder
266: // only creation is not enough, should be set NB attributes, see MidpPaletteProvider for example
267: Debug
268: .warning(catID
269: + " should be initialized! See MidpPaletteProvider."); // NOI18N
270: try {
271: catFO = DataFolder.create(rootFolder, catID)
272: .getPrimaryFile();
273: } catch (IOException ex) {
274: Debug
275: .warning("Can't create folder for palette category: "
276: + ex); // NOI18N
277: }
278: }
279:
280: try {
281: catFO.setAttribute("isReadonly", "true"); // NOI18N
282: } catch (IOException ex) {
283: Debug
284: .warning(
285: "Can't set attributes for palette category folder",
286: ex); // NOI18N
287: }
288:
289: StringBuffer path = new StringBuffer();
290: path.append(catFO.getPath());
291: path.append('/'); // NOI18N
292: path.append(producerID);
293: path.append('.'); // NOI18N
294: path.append(PaletteItemDataLoader.EXTENSION);
295: if (fs.findResource(path.toString()) == null) {
296: try {
297: FileObject itemFO = catFO.createData(producerID,
298: PaletteItemDataLoader.EXTENSION);
299:
300: Properties props = new Properties();
301: props.setProperty("producerID", producerID); // NOI18N
302: String displayName = producer
303: .getPaletteDescriptor().getDisplayName();
304: props.setProperty("displayName",
305: displayName != null ? displayName : ""); // NOI18N
306: String toolTip = producer.getPaletteDescriptor()
307: .getToolTip();
308: props.setProperty("toolTip",
309: toolTip != null ? toolTip : ""); // NOI18N
310: String icon = producer.getPaletteDescriptor()
311: .getSmallIcon();
312: props.setProperty("icon", icon != null ? icon : ""); // NOI18N
313: String largeIcon = producer.getPaletteDescriptor()
314: .getLargeIcon();
315: props.setProperty("bigIcon",
316: largeIcon != null ? largeIcon : ""); // NOI18N
317: FileLock lock = itemFO.lock();
318: OutputStream os = null;
319: try {
320: os = itemFO.getOutputStream(lock);
321: props.store(os, "VMD Palette Item"); // NOI18N
322: } finally {
323: if (os != null) {
324: os.close();
325: }
326: lock.releaseLock();
327: }
328: } catch (IOException e) {
329: StringBuffer str = new StringBuffer();
330: str.append("Can't create file for palette item: "); // NOI18N
331: str.append(path);
332: str.append(", "); // NOI18N
333: str.append(producerID);
334: str.append("."); // NOI18N
335: str.append(PaletteItemDataLoader.EXTENSION);
336: str.append(": "); // NOI18N
337: str.append(e);
338: Debug.warning(str.toString());
339: }
340: }
341: }
342: }
343:
344: void checkValidity(final Lookup lookup) {
345: PaletteItemDataNode node = lookup
346: .lookup(PaletteItemDataNode.class);
347: assert node != null;
348:
349: final String producerID = node.getProducerID();
350: if (producerID == null) {
351: node.setNeedCheck(false);
352: node.setValid(false);
353: return;
354: }
355:
356: if (!nodesMap.containsKey(producerID)) {
357: nodesMap.put(producerID, node);
358: }
359:
360: node.setNeedCheck(false);
361: scheduleCheckValidityCore(lookup);
362: }
363:
364: private void scheduleCheckValidityCore(Lookup lookup) {
365: synchronized (validationQueue) {
366: validationQueue.add(lookup);
367: if (isValidationRunning) {
368: return;
369: }
370: isValidationRunning = true;
371: }
372: RequestProcessor.getDefault().post(this );
373: }
374:
375: public void run() {
376: while (true) {
377: Lookup lookup;
378: synchronized (validationQueue) {
379: if (validationQueue.isEmpty()) {
380: isValidationRunning = false;
381: break;
382: }
383: lookup = validationQueue.remove();
384: }
385: checkValidityCore(lookup);
386: }
387:
388: SwingUtilities.invokeLater(new Runnable() {
389:
390: public void run() {
391: refreshPaletteController();
392: }
393: });
394: }
395:
396: private void checkValidityCore(Lookup lookup) {
397: if (activeDocument == null || activeDocument.get() == null) {
398: return;
399: }
400:
401: PaletteItemDataNode node = lookup
402: .lookup(PaletteItemDataNode.class);
403: if (node == null) {
404: return;
405: }
406:
407: final String producerID = node.getProducerID();
408: String projectID = activeDocument.get().getDocumentInterface()
409: .getProjectID();
410: String projectType = activeDocument.get()
411: .getDocumentInterface().getProjectType();
412:
413: // check whether producerID is valid
414: final ComponentProducer[] result = new ComponentProducer[1];
415: final DescriptorRegistry registry = DescriptorRegistry
416: .getDescriptorRegistry(projectType, projectID);
417: registry.readAccess(new Runnable() {
418:
419: public void run() {
420: List<ComponentProducer> producers = registry
421: .getComponentProducers();
422: ComponentProducer producer = null;
423: for (ComponentProducer p : producers) {
424: if (p.getProducerID().equals(producerID)) {
425: producer = p;
426: break;
427: }
428: }
429: result[0] = producer;
430: }
431: });
432:
433: Boolean isValid = result[0] != null;
434:
435: // check component's availability in classpath
436: if (isValid) {
437: isValid = result[0].checkValidity(activeDocument.get(),
438: false);
439: }
440:
441: node.setValid(isValid == null || isValid);
442: }
443:
444: void clearNodesStateCache() {
445: for (PaletteItemDataNode node : nodesMap.values()) {
446: node.setNeedCheck(true);
447: node.setValid(true);
448: }
449: }
450:
451: void setActiveDocument(DesignDocument activeDocument) {
452: this .activeDocument = new WeakReference<DesignDocument>(
453: activeDocument);
454: }
455:
456: public void resultChanged(LookupEvent ev) {
457: init();
458: }
459:
460: private class Actions extends PaletteActions {
461:
462: public Action[] getImportActions() {
463: if (activeDocument == null || activeDocument.get() == null) {
464: return null;
465: }
466:
467: String projectType = activeDocument.get()
468: .getDocumentInterface().getProjectType();
469:
470: Collection<? extends PaletteProvider> providers = Lookup
471: .getDefault().lookupAll(PaletteProvider.class);
472: ArrayList<Action> actions = new ArrayList<Action>();
473: for (PaletteProvider paletteProvider : providers) {
474: List<? extends Action> list = paletteProvider
475: .getActions(projectType);
476: if (list != null) {
477: actions.addAll(list);
478: }
479: }
480: return actions.toArray(new Action[actions.size()]);
481: }
482:
483: public Action[] getCustomPaletteActions() {
484: return new Action[0];
485: }
486:
487: public Action[] getCustomCategoryActions(Lookup category) {
488: return new Action[0];
489: }
490:
491: public Action[] getCustomItemActions(Lookup item) {
492: return new Action[0];
493: }
494:
495: public Action getPreferredAction(Lookup item) {
496: return null;
497: }
498:
499: @Override
500: public Action getRefreshAction() {
501: return new AbstractAction() {
502:
503: public void actionPerformed(ActionEvent evt) {
504: refreshDescriptorRegistry();
505: refresh();
506: }
507: };
508: }
509:
510: @Override
511: public Action getResetAction() {
512: return new AbstractAction() {
513:
514: public void actionPerformed(ActionEvent evt) {
515: refreshDescriptorRegistry();
516: try {
517: fs.runAtomicAction(new AtomicAction() {
518:
519: public void run() {
520: clean();
521: init();
522: }
523: });
524: } catch (IOException e) {
525: Debug.warning(e);
526: }
527: }
528: };
529: }
530: }
531:
532: private class Filter extends PaletteFilter {
533:
534: public boolean isValidCategory(Lookup lkp) {
535: return true;
536: }
537:
538: public boolean isValidItem(Lookup lkp) {
539: PaletteItemDataNode node = lkp
540: .lookup(PaletteItemDataNode.class);
541: return node == null || node.isValid();
542: }
543: }
544:
545: private class DNDHandler extends DragAndDropHandler {
546:
547: public void customize(final ExTransferable t, Lookup item) {
548: if (activeDocument == null || activeDocument.get() == null) {
549: return;
550: }
551:
552: PaletteItemDataObject itemDataObject = item
553: .lookup(PaletteItemDataObject.class);
554: if (itemDataObject == null) {
555: return;
556: }
557:
558: final String producerID = itemDataObject.getProducerID();
559: String projectID = activeDocument.get()
560: .getDocumentInterface().getProjectID();
561: String projectType = activeDocument.get()
562: .getDocumentInterface().getProjectType();
563: final DescriptorRegistry registry = DescriptorRegistry
564: .getDescriptorRegistry(projectType, projectID);
565:
566: registry.readAccess(new Runnable() {
567:
568: public void run() {
569: List<ComponentProducer> producers = registry
570: .getComponentProducers();
571: final ComponentProducer[] producer = new ComponentProducer[1];
572: for (ComponentProducer p : producers) {
573: if (p.getProducerID().equals(producerID)) {
574: producer[0] = p;
575: break;
576: }
577: }
578:
579: if (producer[0] != null) {
580: DefaultDataFlavor dataFlavor = new DefaultDataFlavor(
581: producer[0]);
582: t.put(new ExTransferable.Single(dataFlavor) {
583:
584: protected Object getData() {
585: return producer[0].getProducerID();
586: }
587: });
588: }
589: }
590: });
591: }
592: }
593: }
|