0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.visualweb.complib;
0043:
0044: import java.awt.Image;
0045: import java.beans.BeanDescriptor;
0046: import java.beans.BeanInfo;
0047: import java.beans.Introspector;
0048: import java.beans.PropertyChangeListener;
0049: import java.beans.PropertyChangeSupport;
0050: import java.io.File;
0051: import java.io.IOException;
0052: import java.net.MalformedURLException;
0053: import java.net.URI;
0054: import java.net.URL;
0055: import java.util.ArrayList;
0056: import java.util.Collection;
0057: import java.util.Collections;
0058: import java.util.HashMap;
0059: import java.util.HashSet;
0060: import java.util.Iterator;
0061: import java.util.List;
0062: import java.util.Map;
0063: import java.util.Set;
0064: import java.util.SortedSet;
0065: import java.util.TreeSet;
0066: import java.util.logging.Level;
0067: import java.util.prefs.Preferences;
0068:
0069: import javax.swing.Action;
0070: import javax.swing.Icon;
0071: import javax.swing.ImageIcon;
0072: import javax.swing.SwingUtilities;
0073: import javax.swing.event.EventListenerList;
0074:
0075: import org.netbeans.api.project.Project;
0076: import org.netbeans.api.project.ProjectUtils;
0077: import org.netbeans.api.project.libraries.Library;
0078: import org.netbeans.api.project.libraries.LibraryManager;
0079: import org.netbeans.api.project.ui.OpenProjects;
0080: import org.netbeans.modules.visualweb.complib.Complib.Identifier;
0081: import org.netbeans.modules.visualweb.complib.Complib.InitialPaletteFolder;
0082: import org.netbeans.modules.visualweb.complib.Complib.InitialPaletteItem;
0083: import org.netbeans.modules.visualweb.complib.ComplibManifest.EeSpecVersion;
0084: import org.netbeans.modules.visualweb.complib.PaletteUtil.Category;
0085: import org.netbeans.modules.visualweb.complib.PaletteUtil.Item;
0086: import org.netbeans.modules.visualweb.complib.PaletteUtil.Palette;
0087: import org.netbeans.modules.visualweb.complib.api.ComplibEvent;
0088: import org.netbeans.modules.visualweb.complib.api.ComplibException;
0089: import org.netbeans.modules.visualweb.complib.api.ComplibListener;
0090: import org.netbeans.modules.visualweb.complib.api.ComplibService;
0091: import org.netbeans.modules.visualweb.complib.ui.ComplibsRootNode;
0092: import org.netbeans.modules.visualweb.project.jsf.api.JsfProjectUtils;
0093: import org.netbeans.spi.palette.PaletteActions;
0094: import org.netbeans.spi.palette.PaletteFilter;
0095: import org.openide.DialogDisplayer;
0096: import org.openide.NotifyDescriptor;
0097: import org.openide.filesystems.FileObject;
0098: import org.openide.filesystems.FileUtil;
0099: import org.openide.loaders.DataFolder;
0100: import org.openide.loaders.DataObject;
0101: import org.openide.nodes.Node;
0102: import org.openide.util.Lookup;
0103: import org.openide.util.NbBundle;
0104: import org.openide.util.NbPreferences;
0105: import org.openide.util.RequestProcessor;
0106: import org.openide.util.Utilities;
0107:
0108: import com.sun.rave.designtime.BeanCreateInfo;
0109: import com.sun.rave.designtime.BeanCreateInfoSet;
0110: import com.sun.rave.designtime.Constants;
0111: import com.sun.rave.designtime.DisplayItem;
0112:
0113: /**
0114: * Singleton used to manage component libraries. The old name of this class used to be
0115: * ComponentLibraryManger.
0116: *
0117: * @author Edwin Goei
0118: */
0119: public class ComplibServiceProvider implements ComplibService {
0120:
0121: /**
0122: * FileObject attribute set on project dir to specify version of complib implementation for that
0123: * project
0124: */
0125: private static final String ATTR_MIGRATION_VERSION = "org-netbeans-modules-visualweb-complib_migration-version";
0126:
0127: /** Values for ATTR_MIGRATION_VERSION */
0128: enum ProjectComplibVersion {
0129: NETBEANS_6
0130: }
0131:
0132: public static final String ADDABLE_COMPLIBS = "addableComplibs";
0133:
0134: /**
0135: * PaletteFilter used to implement complib versioning. If a project contains a complib with a
0136: * particular namespace URI and a version, this feature prevents components from any new complib
0137: * in the userdir from appearing on the palette if the complib has the same namespace URI but
0138: * different version number. Thus preventing the user from adding it to the project. In the
0139: * future, it may be possible to upgrade a complib.
0140: */
0141: private static class ComplibPaletteFilter extends PaletteFilter {
0142: private final Project project;
0143:
0144: public ComplibPaletteFilter(Project project) {
0145: this .project = project;
0146: }
0147:
0148: public boolean isValidCategory(Lookup lookup) {
0149: if (project == null) {
0150: return true;
0151: }
0152: Node itemNode = lookup.lookup(Node.class);
0153: if (itemNode == null) {
0154: return true;
0155: }
0156: DataObject dataObj = itemNode.getLookup().lookup(
0157: DataObject.class);
0158: if (!(dataObj instanceof DataFolder)) {
0159: return true;
0160: }
0161: DataFolder dataFolder = (DataFolder) dataObj;
0162:
0163: // Categories created by users should always be visible
0164: if (!PaletteUtil.isCreatedByComplib(dataFolder
0165: .getPrimaryFile())) {
0166: return true;
0167: }
0168:
0169: // If any child items are visible then show the category
0170: boolean show = false;
0171: for (DataObject child : dataFolder.getChildren()) {
0172: if (isVisible(child)) {
0173: show = true;
0174: break;
0175: }
0176: }
0177: return show;
0178: }
0179:
0180: public boolean isValidItem(Lookup lookup) {
0181: if (project == null) {
0182: return true;
0183: }
0184: Node itemNode = lookup.lookup(Node.class);
0185: if (itemNode == null) {
0186: return true;
0187: }
0188: DataObject dataObj = itemNode.getLookup().lookup(
0189: DataObject.class);
0190: if (dataObj == null) {
0191: return true;
0192: }
0193:
0194: return isVisible(dataObj);
0195: }
0196:
0197: private boolean isVisible(DataObject dataObj) {
0198: if (dataObj instanceof ComplibPaletteItemDataObject) {
0199: // This is a complib component on the palette
0200: ComplibPaletteItemDataObject cpido = (ComplibPaletteItemDataObject) dataObj;
0201: Complib itemComplib = cpido.getComplib();
0202: if (itemComplib == null) {
0203: // Abnormal condition: complib cannot be found so hide it
0204: return false;
0205: }
0206:
0207: /*
0208: * Get the list of complibs for a project. This part only changes when a complib is
0209: * added or removed from a project and does not need to be recalculated for each
0210: * palette item. Optimize this if this is a bottleneck.
0211: */
0212:
0213: // Check the embedded project complibs
0214: Scope scope;
0215: try {
0216: scope = Scope.getScopeForProject(project);
0217: } catch (IOException e) {
0218: IdeUtil.logWarning(
0219: "Unable to find scope for project", e);
0220: return true;
0221: }
0222: Set<ExtensionComplib> projectComplibs = scope
0223: .getComplibs();
0224: Identifier itemId = itemComplib.getIdentifier();
0225: for (ExtensionComplib complib : projectComplibs) {
0226: if (itemId.equals(complib.getIdentifier())) {
0227: return true;
0228: }
0229: }
0230:
0231: // Check any shared complibs used by the project
0232: Set<SharedComplib> sharedComplibs = SharedComplibState
0233: .getInstance().getSharedComplibs(project);
0234: for (SharedComplib complib : sharedComplibs) {
0235: if (itemId.equals(complib.getIdentifier())) {
0236: return true;
0237: }
0238: }
0239:
0240: return false;
0241: } else {
0242: // By default, show the object
0243: return true;
0244: }
0245: }
0246: }
0247:
0248: private static class ComplibPaletteActions extends PaletteActions {
0249:
0250: @Override
0251: public Action[] getCustomCategoryActions(Lookup category) {
0252: Project project = IdeUtil.getActiveProject();
0253: Node complibsRootNode = ComplibServiceProvider
0254: .getInstance().getComplibsRootNode(project);
0255: return complibsRootNode.getActions(true);
0256: }
0257:
0258: @Override
0259: public Action[] getCustomItemActions(Lookup item) {
0260: return getCustomCategoryActions(item);
0261: }
0262:
0263: @Override
0264: public Action[] getCustomPaletteActions() {
0265: return null;
0266: }
0267:
0268: @Override
0269: public Action[] getImportActions() {
0270: return null;
0271: }
0272:
0273: @Override
0274: public Action getPreferredAction(Lookup item) {
0275: return null;
0276: }
0277:
0278: }
0279:
0280: /**
0281: * Part of interface that ComplibServiceProvider exports to the UI
0282: */
0283: public static class ComponentInfo implements Comparable<Object> {
0284: /** className is unique within the scope of a ComplibImpl */
0285: private String className;
0286:
0287: private HashSet<String> initialCategories = new HashSet<String>();
0288:
0289: /** ComplibImpl this component belongs to */
0290: private Complib complib;
0291:
0292: private String displayName;
0293:
0294: private String tooltip;
0295:
0296: private String helpId;
0297:
0298: private Icon icon;
0299:
0300: private static final Image defaultIconImage = Utilities
0301: .loadImage("org/netbeans/modules/visualweb/palette/resources/custom_component.png");
0302:
0303: private ComponentInfo(String className, Complib complib)
0304: throws ComplibException {
0305: this .className = className;
0306: this .complib = complib;
0307:
0308: // Get info from BeanInfo or BeanCreateInfo/Set
0309:
0310: ClassLoader complibLoader = complib.getClassLoader();
0311:
0312: /*
0313: * 6393979 Simulate an appropriate context ClassLoader. The context ClassLoader in
0314: * NetBeans, which is the NetBeans system class loader, is not what components expect so
0315: * temporarily set the context ClassLoader to simulate a Java EE container and restore
0316: * it later.
0317: */
0318: Thread currentThread = Thread.currentThread();
0319: ClassLoader origContextLoader = currentThread
0320: .getContextClassLoader();
0321: currentThread.setContextClassLoader(complibLoader);
0322: Class<?> beanClass;
0323: try {
0324: beanClass = Class.forName(className, true,
0325: complibLoader);
0326: } catch (ClassNotFoundException cnfe) {
0327: ComplibException e = new ComplibException(
0328: "Class with name=" + className
0329: + " was not found by classLoader="
0330: + complibLoader, cnfe);
0331: IdeUtil.logWarning(e);
0332: throw e;
0333: } finally {
0334: currentThread.setContextClassLoader(origContextLoader);
0335: }
0336:
0337: try {
0338: Image image;
0339: if (BeanCreateInfo.class.isAssignableFrom(beanClass)
0340: || BeanCreateInfoSet.class
0341: .isAssignableFrom(beanClass)) {
0342: DisplayItem beanCreateInfoOrSet = (DisplayItem) beanClass
0343: .newInstance();
0344: displayName = beanCreateInfoOrSet.getDisplayName();
0345: tooltip = beanCreateInfoOrSet.getDescription();
0346: helpId = beanCreateInfoOrSet.getHelpKey();
0347: image = beanCreateInfoOrSet.getSmallIcon();
0348: } else {
0349: BeanInfo bi = Introspector.getBeanInfo(beanClass);
0350: BeanDescriptor bd = bi.getBeanDescriptor();
0351: displayName = bd.getDisplayName();
0352: tooltip = bd.getShortDescription();
0353: helpId = (String) bd
0354: .getValue(Constants.BeanDescriptor.HELP_KEY);
0355: image = bi.getIcon(BeanInfo.ICON_COLOR_16x16);
0356: }
0357: if (image == null) {
0358: image = defaultIconImage;
0359: }
0360: icon = new ImageIcon(image);
0361: } catch (Exception e) {
0362: throw new ComplibException(
0363: "Unable to access component BeanInfo", e);
0364: }
0365: }
0366:
0367: public int hashCode() {
0368: return getDisplayName().hashCode()
0369: + getClassName().hashCode();
0370: }
0371:
0372: public boolean equals(Object anObject) {
0373: if (this == anObject) {
0374: return true;
0375: }
0376: if (anObject instanceof ComponentInfo) {
0377: return compareTo(anObject) == 0;
0378: }
0379: return false;
0380: }
0381:
0382: public String getClassName() {
0383: return className;
0384: }
0385:
0386: public Icon getIcon() {
0387: return icon;
0388: }
0389:
0390: public String getDisplayName() {
0391: return displayName;
0392: }
0393:
0394: public String getTooltip() {
0395: return tooltip;
0396: }
0397:
0398: public String getHelpId() {
0399: return helpId;
0400: }
0401:
0402: public Set<String> getInitialCategories() {
0403: return initialCategories;
0404: }
0405:
0406: private void addInitialCategory(String category) {
0407: initialCategories.add(category);
0408: }
0409:
0410: public Complib getComplib() {
0411: return complib;
0412: }
0413:
0414: public int compareTo(Object o) {
0415: // Order by display name then className
0416: ComponentInfo anotherComponentInfo = (ComponentInfo) o;
0417: int dispNameCompare = getDisplayName().compareTo(
0418: anotherComponentInfo.getDisplayName());
0419:
0420: // If display name is equal, then sort based on className
0421: if (dispNameCompare == 0) {
0422: return getClassName().compareTo(
0423: anotherComponentInfo.getClassName());
0424: } else {
0425: return dispNameCompare;
0426: }
0427: }
0428: }
0429:
0430: /**
0431: * Represents a collection of related user scope complibs which have in common the same
0432: * namespace URI but different versions.
0433: *
0434: * @author Edwin Goei
0435: */
0436: public static class RelatedComplibs {
0437: private List<ExtensionComplib> newerComplibs;
0438:
0439: private List<ExtensionComplib> olderComplibs;
0440:
0441: private ExtensionComplib sameVersionComplib;
0442:
0443: private RelatedComplibs(List<ExtensionComplib> newerComplibs,
0444: List<ExtensionComplib> olderComplibs,
0445: ExtensionComplib sameVersionComplib) {
0446: this .newerComplibs = newerComplibs;
0447: this .olderComplibs = olderComplibs;
0448: this .sameVersionComplib = sameVersionComplib;
0449: }
0450:
0451: public List<ExtensionComplib> getNewerComplibs() {
0452: return newerComplibs;
0453: }
0454:
0455: public List<ExtensionComplib> getOlderComplibs() {
0456: return olderComplibs;
0457: }
0458:
0459: public ExtensionComplib getSameVersionComplib() {
0460: return sameVersionComplib;
0461: }
0462: }
0463:
0464: private static final File complibsToInstallDir = new File(IdeUtil
0465: .getNetBeansInstallDirectory(), "complibs_to_install"); // NO18N
0466:
0467: private static final String COMPLIB_EXTENSION = "complib"; // NOI18N
0468:
0469: private static final ComponentInfo[] EMPTY_COMPONENT_INFO_ARRAY = new ComponentInfo[0];
0470:
0471: private EventListenerList listenerList = new EventListenerList();
0472:
0473: private PropertyChangeSupport pceListeners = new PropertyChangeSupport(
0474: this );
0475:
0476: /** Scope object where user imported complib files live */
0477: private Scope userScope;
0478:
0479: /** Stores preferences for the complib module */
0480: private Preferences preferences;
0481:
0482: public static ComplibServiceProvider getInstance() {
0483: return (ComplibServiceProvider) Lookup.getDefault().lookup(
0484: ComplibService.class);
0485: }
0486:
0487: /**
0488: * This should only be called once via ComplibService Lookup.
0489: *
0490: * @throws IOException
0491: */
0492: public ComplibServiceProvider() throws IOException {
0493: /*
0494: * Note: no code executed as a result of calling this constructor should call back into this
0495: * constructor. For example, do not call ComplibServiceProvider.getInstance() or else this
0496: * will cause initialization problems.
0497: */
0498:
0499: initUserScope();
0500:
0501: installNewComplibPackages(complibsToInstallDir);
0502: }
0503:
0504: private void initUserScope() throws IOException {
0505: File userInstallDir = new File(IdeUtil.getComplibStateDir(),
0506: "installed-complibs");
0507: if (!userInstallDir.isDirectory()) {
0508: // Try to migrate installed complibs from old to new location
0509: String userDir = System.getProperty("netbeans.user"); // NOI18N
0510: if (userDir != null) {
0511: File oldInstallDir = new File(userDir, "complibs"); // NOI18N
0512: if (oldInstallDir.exists()) {
0513: IdeUtil.copyFileRecursive(oldInstallDir,
0514: userInstallDir);
0515:
0516: // Try to remove the old directory
0517: if (!IdeUtil.deleteRecursive(oldInstallDir)) {
0518: IdeUtil
0519: .logWarning("Unable to remove directory: "
0520: + oldInstallDir);
0521: }
0522: }
0523: }
0524: }
0525:
0526: userScope = Scope.createScope(userInstallDir);
0527: }
0528:
0529: private void initSharedComplibs(Set<Project> projects) {
0530: for (Project project : projects) {
0531: // Load in shared complib info used by this project
0532: SharedComplibState.getInstance().getSharedComplibs(project);
0533: }
0534: }
0535:
0536: /**
0537: * Return just the rave VWP projects.
0538: *
0539: * @return
0540: */
0541: private Set<Project> getOpenRaveProjects() {
0542: HashSet<Project> projects = new HashSet<Project>();
0543: Project[] projectsArray = OpenProjects.getDefault()
0544: .getOpenProjects();
0545: for (Project project : projectsArray) {
0546: if (JsfProjectUtils.isJsfProject(project)) {
0547: projects.add(project);
0548: }
0549: }
0550: return projects;
0551: }
0552:
0553: /**
0554: * Make sure the project complibs are installed on the palette.
0555: *
0556: * @param projectComplibs
0557: */
0558: private void ensureProjectComplibsAreInstalled(
0559: Set<ExtensionComplib> projectComplibs) {
0560: ArrayList<ExtensionComplib> al = new ArrayList<ExtensionComplib>();
0561: for (ExtensionComplib projectComplib : projectComplibs) {
0562: if (!userScope.contains(projectComplib)) {
0563: al.add(projectComplib);
0564: }
0565: }
0566:
0567: if (al.isEmpty()) {
0568: // Project complibs are already on user palette
0569: return;
0570: }
0571:
0572: // Inform user
0573: // TODO Not sure this is the right UI
0574: String msg = NbBundle.getMessage(ComplibServiceProvider.class,
0575: "complib.initProjectComplibs"); // NOI18N
0576: if (false) {
0577: NotifyDescriptor nd = new NotifyDescriptor.Message(msg,
0578: NotifyDescriptor.INFORMATION_MESSAGE);
0579: DialogDisplayer.getDefault().notify(nd);
0580: } else {
0581: // Too many dialogs are distracting so just log the event
0582: IdeUtil.getLogger().log(Level.INFO, msg);
0583: }
0584:
0585: // Install complibs into user scope and the palette
0586: for (Iterator<ExtensionComplib> iter = al.iterator(); iter
0587: .hasNext();) {
0588: ExtensionComplib projectCompLib = iter.next();
0589: try {
0590: installProjectComplib(projectCompLib);
0591: } catch (Exception e) {
0592: // Output warning to IDE log
0593: IdeUtil.logWarning(e);
0594: }
0595: }
0596: }
0597:
0598: /**
0599: * Ensures that a Library Ref to a user scoped complib for a project exists and it may also
0600: * create the associated Lib Def, if needed.
0601: *
0602: * @param project
0603: * @param userComplib
0604: * @throws IOException
0605: */
0606: private void addLibraryRefAndDef(Project project,
0607: ExtensionComplib userComplib) throws IOException {
0608: String libName = deriveUniqueLibraryName(userComplib);
0609: Library libDef = LibraryManager.getDefault()
0610: .getLibrary(libName);
0611: if (libDef == null) {
0612: /*
0613: * Library Defs are created when complib is installed into userdir (since NB 6).
0614: * However, it is still possible that the user removes the Lib Def afterward and so we
0615: * re-create it here in case that happens.
0616: */
0617: libDef = createUserScopeLibDef(userComplib);
0618: }
0619: JsfProjectUtils.addLibraryReferences(project,
0620: new Library[] { libDef });
0621: }
0622:
0623: /**
0624: * Derive unique name for global NB Library Defs.
0625: *
0626: * @param complib
0627: * User scope complib
0628: * @return
0629: */
0630: private String deriveUniqueLibraryName(ExtensionComplib complib) {
0631: // assert: complib is in user scope
0632: return complib.getDirectoryBaseName();
0633: }
0634:
0635: private List<URL> fileListToUrlList(List<File> path)
0636: throws MalformedURLException {
0637: ArrayList<URL> urlList = new ArrayList<URL>(path.size());
0638: for (File file : path) {
0639: urlList.add(file.toURI().toURL());
0640: }
0641: return urlList;
0642: }
0643:
0644: /**
0645: * Attempt to install any new complib package files in the specified directory. Any problems are
0646: * logged and the problem complib is then skipped.
0647: *
0648: * @param dir
0649: * Directory that contains complib package files
0650: */
0651: private void installNewComplibPackages(final File dir) {
0652: if (dir == null || !dir.exists()) {
0653: return;
0654: }
0655:
0656: /*
0657: * Typically, this code will run in the SwingUtilities.isEventDispatchThread() which causes
0658: * problems when modifying the palette so we run this in a new thread.
0659: */
0660: RequestProcessor.getDefault().post(new Runnable() {
0661: public void run() {
0662: // TODO Need to have modified timestamp per complib
0663: long scopeLastModified = userScope.getLastModified();
0664:
0665: FileObject complibFo = FileUtil.toFileObject(dir);
0666: FileObject[] children = complibFo.getChildren();
0667: for (int i = 0; i < children.length; i++) {
0668: FileObject fo = children[i];
0669: File absFile = FileUtil.toFile(fo);
0670: // Install any new unexpanded complib files
0671: if (COMPLIB_EXTENSION.equals(fo.getExt())) {
0672: if (absFile.lastModified() > scopeLastModified) {
0673: try {
0674: ComplibPackage pkg = new ComplibPackage(
0675: absFile);
0676: installComplibPackage(pkg);
0677: } catch (Exception e) {
0678: // Log the exception and continue
0679: IdeUtil.logError(e);
0680: continue;
0681: }
0682: }
0683: }
0684: }
0685: }
0686:
0687: });
0688: }
0689:
0690: /**
0691: * Return all installed component libraries. Installed means built-in or in user scope.
0692: *
0693: * @return
0694: */
0695: public ArrayList<Complib> getInstalledComplibs() {
0696: ArrayList<Complib> complibs = new ArrayList<Complib>();
0697: // TODO resurrect notion of built-in component libraries
0698: // complibs.add(BuiltInComplib.getInstance());
0699: complibs.addAll(userScope.getComplibs());
0700: return complibs;
0701: }
0702:
0703: /**
0704: * Installed complib means a user-scoped complib or the built-in complib
0705: *
0706: * @param id
0707: * @return installed complib with specified identifier or null if not found
0708: */
0709: private Complib getInstalledComplib(Complib.Identifier id) {
0710: // FIXME Figure out what to do with built-in complibs
0711: // BuiltInComplib builtInCompLib = BuiltInComplib.getInstance();
0712: // if (builtInCompLib.getIdentifier().equals(id)) {
0713: // return builtInCompLib;
0714: // }
0715:
0716: // Check user scope
0717: Set<ExtensionComplib> complibs = userScope.getComplibs();
0718: for (ExtensionComplib complib : complibs) {
0719: if (complib.getIdentifier().equals(id)) {
0720: return complib;
0721: }
0722: }
0723:
0724: // Check any shared complibs
0725: /*
0726: * XXX Warning this may cause some interaction problems with older complib code.
0727: */
0728: HashSet<SharedComplib> shComplibs = SharedComplibState
0729: .getInstance().getAllSharedComplibs();
0730: for (SharedComplib complib : shComplibs) {
0731: if (complib.getIdentifier().equals(id)) {
0732: return complib;
0733: }
0734: }
0735:
0736: return null;
0737: }
0738:
0739: public void installComplibFile(File complibFile, boolean overwrite)
0740: throws ComplibException {
0741: try {
0742: ComplibPackage pkg = new ComplibPackage(complibFile);
0743: if (isInstalled(pkg) && !overwrite) {
0744: return;
0745: }
0746: installComplibPackage(pkg);
0747: } catch (IOException e) {
0748: throw new ComplibException(
0749: "Unable to install complib file", e);
0750: }
0751: }
0752:
0753: /**
0754: * Install a complib package into user scope and into the palette overwriting any existing
0755: * complib and handling any failures by undoing changes.
0756: *
0757: * @param pkg
0758: * complib package
0759: * @throws ComplibException
0760: * @throws IOException
0761: */
0762: public ExtensionComplib installComplibPackage(ComplibPackage pkg)
0763: throws ComplibException, IOException {
0764: Identifier identifer = pkg.getIdentifer();
0765: removeExistingInstalledComplib(identifer);
0766: ExtensionComplib complib = userScope.installComplibPackage(pkg);
0767: finishInstallToUserScope(complib);
0768: return complib;
0769: }
0770:
0771: /**
0772: * Install a project scoped complib into user scope and into the palette overwriting any
0773: * existing complib and handling any failures by undoing changes.
0774: *
0775: * @param projectComplib
0776: * expanded project scoped complib
0777: * @throws ComplibException
0778: * @throws IOException
0779: */
0780: private void installProjectComplib(ExtensionComplib projectComplib)
0781: throws ComplibException, IOException {
0782: Identifier identifer = projectComplib.getIdentifier();
0783: removeExistingInstalledComplib(identifer);
0784: ExtensionComplib newComplib = userScope
0785: .installComplib(projectComplib);
0786: finishInstallToUserScope(newComplib);
0787: }
0788:
0789: private void finishInstallToUserScope(ExtensionComplib userComplib)
0790: throws ComplibException, IOException, MalformedURLException {
0791: /*
0792: * Temporarily install the complib into the system so that UI Item-s can be created and then
0793: * rollback if a problem occurs
0794: */
0795: try {
0796: addToPalette(userComplib);
0797: } catch (ComplibException e1) {
0798: // Rollback
0799: remove(userComplib);
0800: throw e1;
0801: }
0802: JavaHelpStorage.installComplibHelp(userComplib);
0803:
0804: // Create Lib Def
0805: createUserScopeLibDef(userComplib);
0806:
0807: fireAddableComplibsChanged(userScope);
0808: }
0809:
0810: /**
0811: * Creates a new Lib Def. Any existing Lib Def with the same name will be removed first.
0812: *
0813: * @param complib
0814: * @return
0815: * @throws IOException
0816: * @throws MalformedURLException
0817: */
0818: private Library createUserScopeLibDef(ExtensionComplib complib)
0819: throws IOException, MalformedURLException {
0820: String libName = deriveUniqueLibraryName(complib);
0821:
0822: Library libDef = LibraryManager.getDefault()
0823: .getLibrary(libName);
0824: if (libDef != null) {
0825: // If one exists, remove it first
0826: JsfProjectUtils.removeLibrary(libName);
0827: }
0828:
0829: // Use unique libName as a key for the description.
0830: String description = complib.getVersionedTitle();
0831: LibraryLocalizationBundle.add(libName, description);
0832:
0833: String localizingBundle = LibraryLocalizationBundle.class
0834: .getName();
0835: List<URL> rtPath = fileListToUrlList(complib.getRuntimePath());
0836: List<URL> dtPath = fileListToUrlList(complib
0837: .getDesignTimePath());
0838: List<URL> javadocPath = fileListToUrlList(complib
0839: .getJavadocPath());
0840: List<URL> sourcePath = fileListToUrlList(complib
0841: .getSourcePath());
0842: return JsfProjectUtils.createComponentLibrary(libName, libName,
0843: localizingBundle, rtPath, sourcePath, javadocPath,
0844: dtPath);
0845: }
0846:
0847: /**
0848: * Replace any existing installed complib with the same identifier
0849: *
0850: * @param identifer
0851: * @throws ComplibException
0852: */
0853: private void removeExistingInstalledComplib(Identifier identifer)
0854: throws ComplibException {
0855: Complib existingCompLib = getInstalledComplib(identifer);
0856: if (existingCompLib != null) {
0857: if (existingCompLib instanceof ExtensionComplib) {
0858: ExtensionComplib extCompLib = (ExtensionComplib) existingCompLib;
0859: remove(extCompLib);
0860: } else {
0861: throw new ComplibException(
0862: "Cannot install non-extension complib with identifier: "
0863: + identifer);
0864: }
0865: }
0866: }
0867:
0868: /**
0869: * Add components in complib to user palette(s).
0870: *
0871: * @param complib
0872: * @throws ComplibException
0873: */
0874: private void addToPalette(Complib complib) throws ComplibException {
0875: // Get the palette roots to add categories to
0876: List<Palette> palRoots = PaletteUtil.getPaletteRoots(complib);
0877:
0878: /*
0879: * Process the folders in reverse order so that if they are new, then they will appear in
0880: * the correct order on the palette.
0881: */
0882: List<InitialPaletteFolder> topFolders = complib
0883: .getComponentItemsInFolders();
0884: Collections.reverse(topFolders);
0885:
0886: for (InitialPaletteFolder folder : topFolders) {
0887: String catName = folder.getName();
0888:
0889: for (Palette pal : palRoots) {
0890: Category dstPalCat = pal.getOrCreateCategory(catName);
0891: for (InitialPaletteItem item : folder.getChildren()) {
0892: mayBeCreatePaletteItemRecurse(dstPalCat, complib,
0893: item);
0894: }
0895: }
0896: }
0897:
0898: // Notify listeners that the palette has changed
0899: firePaletteChanged(new ComplibEvent(complib));
0900: }
0901:
0902: private void mayBeCreatePaletteItemRecurse(Category cat,
0903: Complib complib, InitialPaletteItem initItem) {
0904: // This method can support a hierarchical palette but NetBeans palette
0905: // does not support hierarchy so we place all child items in the same
0906: // category.
0907: // TODO remove this restriction in future.
0908:
0909: String itemClassName = initItem.getClassName();
0910:
0911: // 6200271 Don't add to the palette, this may be an abstract
0912: // base component
0913: if (complib.isHidden(itemClassName)) {
0914: return;
0915: }
0916:
0917: try {
0918: ComponentInfo compInfo = new ComponentInfo(itemClassName,
0919: complib);
0920: cat.createItem(compInfo);
0921: } catch (Exception e) {
0922: // Warn and skip this item
0923: IdeUtil.logWarning(e);
0924: return;
0925: }
0926:
0927: // Recurse by creating Item-s for each child
0928: List<InitialPaletteItem> children = initItem.getChildren();
0929: for (InitialPaletteItem item : children) {
0930: mayBeCreatePaletteItemRecurse(cat, complib, item);
0931: }
0932:
0933: return;
0934: }
0935:
0936: /**
0937: * Remove all components in a complib from user scope and the UI
0938: *
0939: * @param complib
0940: */
0941: public void remove(ExtensionComplib complib) {
0942: // Remove from the UI
0943: removeFromPaletteCategories(complib);
0944:
0945: // Remove from IDE Help
0946: try {
0947: JavaHelpStorage.uninstallComplibHelp(complib);
0948: } catch (IOException e) {
0949: IdeUtil
0950: .logWarning(
0951: "Unable to remove component library Help from system",
0952: e);
0953: }
0954:
0955: // Remove from user scope
0956: userScope.remove(complib);
0957:
0958: // Remove the corresponding Library Definition
0959: String libName = deriveUniqueLibraryName(complib);
0960: Library libDef = LibraryManager.getDefault()
0961: .getLibrary(libName);
0962: if (libDef != null) {
0963: try {
0964: JsfProjectUtils.removeLibrary(libName);
0965: } catch (IOException e) {
0966: IdeUtil.logWarning(
0967: "Unable to remove Library Definition: "
0968: + libName, e);
0969: }
0970: }
0971:
0972: fireAddableComplibsChanged(userScope);
0973: }
0974:
0975: /**
0976: * Iterate through all palette categories and delete PaletteItems associated with a complib
0977: * having the same Id. Idempotent. Also removes any empty palette categories that were
0978: * automatically created by the complib module.
0979: *
0980: * @param compLibId
0981: */
0982: private void removeFromPaletteCategories(Complib complib) {
0983: Complib.Identifier compLibId = complib.getIdentifier();
0984: for (Category palCat : PaletteUtil.getAllCategories()) {
0985: for (Item palItem : palCat.getChildren()) {
0986: if (palItem.getComplibId().equals(compLibId)) {
0987: palItem.remove();
0988: }
0989: }
0990:
0991: // Remove the palette category if it's empty
0992: if (palCat.isCreatedByComplib()
0993: && palCat.getChildren().isEmpty()) {
0994: palCat.remove();
0995: }
0996: }
0997:
0998: // Notify listeners that the palette has changed
0999: firePaletteChanged(new ComplibEvent(complib));
1000: }
1001:
1002: /**
1003: * @param pkg
1004: * ComplibPackage
1005: * @return true iff pkg has the same complib ID as an installed or the built-in component
1006: * library
1007: */
1008: public boolean isInstalled(ComplibPackage pkg) {
1009: return isInstalled(pkg.getIdentifer());
1010: }
1011:
1012: /**
1013: * @param complibId
1014: * Complib identifier
1015: * @return true iff Id is same as an installed or the built-in component library
1016: */
1017: private boolean isInstalled(Complib.Identifier complibId) {
1018: return getInstalledComplib(complibId) != null;
1019: }
1020:
1021: /**
1022: * Returns information about each component contained in a complib in sorted order by display
1023: * name.
1024: *
1025: * @param complib
1026: * @return
1027: * @throws ComplibException
1028: */
1029: public ComponentInfo[] getComponentInfos(Complib complib)
1030: throws ComplibException {
1031: // Maps String className to ComponentInfo
1032: HashMap<String, ComponentInfo> compInfoMap = new HashMap<String, ComponentInfo>();
1033:
1034: // Populate compInfoMap and also determine initial palette state
1035: for (InitialPaletteFolder folder : complib
1036: .getComponentItemsInFolders()) {
1037: String catName = folder.getName();
1038: getUniqueInitialItems(compInfoMap, complib, catName, folder
1039: .getChildren());
1040: }
1041:
1042: // Sort the returned components
1043: SortedSet<ComponentInfo> compInfos = new TreeSet<ComponentInfo>(
1044: compInfoMap.values());
1045:
1046: return compInfos.toArray(EMPTY_COMPONENT_INFO_ARRAY);
1047: }
1048:
1049: private void getUniqueInitialItems(
1050: Map<String, ComponentInfo> compInfoMap, Complib complib,
1051: String folderName, List<InitialPaletteItem> items) {
1052: for (InitialPaletteItem item : items) {
1053: String className = item.getClassName();
1054: ComponentInfo compInfo = compInfoMap.get(className);
1055: if (compInfo == null) {
1056: // Not in Map so add an entry
1057: try {
1058: compInfo = new ComponentInfo(className, complib);
1059: } catch (ComplibException e) {
1060: // Skip if there are any problems
1061: IdeUtil.logError(e);
1062: continue;
1063: }
1064: compInfoMap.put(className, compInfo);
1065: }
1066:
1067: compInfo.addInitialCategory(folderName);
1068:
1069: // Supports hierarchical palette
1070: getUniqueInitialItems(compInfoMap, complib, folderName,
1071: item.getChildren());
1072: }
1073: }
1074:
1075: /**
1076: * Reset the palette state of a currently installed complib to a predefined initial state.
1077: *
1078: * @param complib
1079: * @throws ComplibException
1080: */
1081: public void resetToInitialPalette(Complib complib)
1082: throws ComplibException {
1083: assert isInstalled(complib.getIdentifier());
1084: removeFromPaletteCategories(complib);
1085: addToPalette(complib);
1086: }
1087:
1088: /**
1089: * Remove any existing Library Ref and the complib itself from the project scope.
1090: *
1091: * @param project
1092: * @param prjComplib
1093: * @throws IOException
1094: */
1095: private void removeComplibFromProject0(Project project,
1096: ExtensionComplib prjComplib) throws IOException {
1097:
1098: ExtensionComplib userComplib = userScope
1099: .getExistingComplib(prjComplib);
1100: if (userComplib == null) {
1101: IdeUtil
1102: .logWarning("Unable to find installed component library in userdir: "
1103: + prjComplib.getVersionedTitle());
1104: } else {
1105: String libName = deriveUniqueLibraryName(userComplib);
1106: Library libDef = JsfProjectUtils.getProjectLibraryManager(
1107: project).getLibrary(libName);
1108: if (libDef != null) {
1109: JsfProjectUtils.removeLibraryReferences(project,
1110: new Library[] { libDef });
1111: }
1112: }
1113:
1114: Scope scope = Scope.getScopeForProject(project);
1115: scope.remove(prjComplib);
1116: fireAddableComplibsChanged(scope);
1117:
1118: // TODO remove any project resources that were originally
1119: // installed
1120: }
1121:
1122: /**
1123: * Copy an installed userdir complib into a project and add it to the project classpath.
1124: *
1125: * @param userDirExtComplib
1126: * @param project
1127: * @return
1128: * @throws IOException
1129: * @throws ComplibException
1130: */
1131: private ExtensionComplib copyComplibToProject(
1132: ExtensionComplib userDirExtComplib, Project project)
1133: throws IOException, ComplibException {
1134: Scope scope = Scope.getScopeForProject(project);
1135: ExtensionComplib projectComplib = scope
1136: .installComplib(userDirExtComplib);
1137: fireAddableComplibsChanged(scope);
1138: addLibraryRefAndDef(project, userDirExtComplib);
1139: installProjectResources(userDirExtComplib, project);
1140: return projectComplib;
1141: }
1142:
1143: private void installProjectResources(ExtensionComplib prjCompLib,
1144: Project project) throws IOException {
1145: FileObject docRootFo = JsfProjectUtils.getDocumentRoot(project);
1146: File docRoot = FileUtil.toFile(docRootFo);
1147:
1148: List<File> files = prjCompLib.getWebResourcePath();
1149: for (File file : files) {
1150: if (file.isDirectory()) {
1151: // Note: the named dir itself will be copied into the docRoot
1152: IdeUtil.copyFileRecursive(file, docRoot);
1153: } else {
1154: String baseName = file.getName();
1155: if (baseName.endsWith(".zip")
1156: || baseName.endsWith(".jar")) {
1157: IdeUtil.unzip(file, docRoot);
1158: }
1159: }
1160: }
1161: }
1162:
1163: public Complib getInstalledComplib(String namespaceUri,
1164: String version) {
1165: Complib.Identifier id = new Complib.Identifier(namespaceUri,
1166: version);
1167: return getInstalledComplib(id);
1168: }
1169:
1170: public PaletteFilter createComplibPaletteFilter(Project project) {
1171: return new ComplibPaletteFilter(project);
1172: }
1173:
1174: public PaletteActions createComplibPaletteActions() {
1175: return new ComplibPaletteActions();
1176: }
1177:
1178: public void addComplibListener(ComplibListener listener) {
1179: listenerList.add(ComplibListener.class, listener);
1180: }
1181:
1182: public void removeComplibListener(ComplibListener listener) {
1183: listenerList.remove(ComplibListener.class, listener);
1184: }
1185:
1186: /**
1187: * Safely notifies listeners that the palette has changed by adding a new event to the dispatch
1188: * queue. This avoids a bug such as: 1) user drags an item from the palette, 2) complib gets
1189: * added to project which may change the items displayed on palette so, 3) listeners are
1190: * notified, 4) one listener calls refresh on palette model, 5) new Categories are created, 6)
1191: * drag method has a stale old Category without a parent. Fix is that step #3 is deferred until
1192: * later so that old Category continues to be used during drag processing. Future drag
1193: * processing uses new Category.
1194: *
1195: * To support the project navigator, this event is also overloaded and may mean that a complib
1196: * was added or removed from a project.
1197: *
1198: * @param evt
1199: */
1200: private void firePaletteChanged(final ComplibEvent evt) {
1201: SwingUtilities.invokeLater(new Runnable() {
1202: public void run() {
1203: Object[] listeners = listenerList.getListenerList();
1204: // Each listener occupies two elements - the first is the
1205: // listener class
1206: // and the second is the listener instance
1207: for (int i = 0; i < listeners.length; i += 2) {
1208: if (listeners[i] == ComplibListener.class) {
1209: ((ComplibListener) listeners[i + 1])
1210: .paletteChanged(evt);
1211: }
1212: }
1213: };
1214: });
1215: }
1216:
1217: public Set<ExtensionComplib> getComplibsForProject(Project project) {
1218: Scope scope;
1219: try {
1220: scope = Scope.getScopeForProject(project);
1221: } catch (IOException e) {
1222: // Should not happen
1223: IdeUtil.logWarning(e);
1224: return Collections.emptySet();
1225: }
1226: return scope.getComplibs();
1227: }
1228:
1229: public Node getComplibsRootNode(Project project) {
1230: return new ComplibsRootNode(project);
1231: }
1232:
1233: /**
1234: * Remove an embedded project complib from a project and inform listeners.
1235: *
1236: * @param project
1237: * @param complib
1238: * @throws IOException
1239: */
1240: public void removeComplibFromProject(Project project,
1241: ExtensionComplib complib) throws IOException {
1242: try {
1243: removeComplibFromProject0(project, complib);
1244: } catch (IOException e) {
1245: throw e;
1246: } finally {
1247: // Notify listeners that the set of complibs changed
1248: firePaletteChanged(new ComplibEvent(complib));
1249: }
1250: }
1251:
1252: /**
1253: * Returns a list of currently installed complibs that have the same namespace as the complib
1254: * parameter. The lists are sorted in highest version first order.
1255: *
1256: * @param complib
1257: * @return
1258: */
1259: public RelatedComplibs getRelatedComplibs(ExtensionComplib complib) {
1260: Identifier this Id = complib.getIdentifier();
1261: URI this Namespace = this Id.getNamespaceUri();
1262: Version this Version = this Id.getVersion();
1263:
1264: ArrayList<ExtensionComplib> newerComplibs = new ArrayList<ExtensionComplib>();
1265: ArrayList<ExtensionComplib> olderComplibs = new ArrayList<ExtensionComplib>();
1266: ExtensionComplib sameVersionComplib = null;
1267: Set<ExtensionComplib> userComplibs = userScope.getComplibs();
1268: for (ExtensionComplib iComplib : userComplibs) {
1269: Identifier iId = iComplib.getIdentifier();
1270: if (iId.getNamespaceUri().equals(this Namespace)) {
1271: int cmp = iId.getVersion().compareTo(this Version);
1272: if (cmp > 0) {
1273: newerComplibs.add(iComplib);
1274: } else if (cmp < 0) {
1275: olderComplibs.add(iComplib);
1276: } else {
1277: sameVersionComplib = iComplib;
1278: }
1279: }
1280: }
1281:
1282: // Return the newer complib version first
1283: Collections.sort(newerComplibs, Collections.reverseOrder());
1284: Collections.sort(olderComplibs, Collections.reverseOrder());
1285: return new RelatedComplibs(newerComplibs, olderComplibs,
1286: sameVersionComplib);
1287: }
1288:
1289: /**
1290: * Replace a project-scoped complib with a new installed user-scoped complib. They share the
1291: * same namespace. A null origComplib means to just add newComplib to the project.
1292: *
1293: * @param project
1294: * @param origComplib
1295: * original project-scoped complib, may be null
1296: * @param newComplib
1297: * new installed user-scoped complib
1298: * @throws IOException
1299: * @throws ComplibException
1300: */
1301: public void replaceProjectComplib(Project project,
1302: ExtensionComplib origComplib, ExtensionComplib newComplib)
1303: throws IOException, ComplibException {
1304: ExtensionComplib projectComplib = null;
1305: try {
1306: if (origComplib != null) {
1307: removeComplibFromProject0(project, origComplib);
1308: }
1309: projectComplib = copyComplibToProject(newComplib, project);
1310: } catch (IOException e) {
1311: throw e;
1312: } catch (ComplibException e) {
1313: throw e;
1314: } finally {
1315: // Notify listeners that the set of complibs changed
1316: firePaletteChanged(new ComplibEvent(projectComplib));
1317: }
1318: }
1319:
1320: /**
1321: * Returns a list of installed user-imported complibs that may still be added to a project.
1322: * Complibs in a different namespace from any other complib in the project may still be added to
1323: * a project. Also, enforce Java EE spec version constraint.
1324: *
1325: * @param project
1326: * @return
1327: */
1328: public List<ExtensionComplib> getAddableComplibs(Project project) {
1329: boolean isNonEe5Project = !JsfProjectUtils.JAVA_EE_5
1330: .equals(JsfProjectUtils.getJ2eePlatformVersion(project));
1331:
1332: HashSet<ExtensionComplib> result = new HashSet<ExtensionComplib>(
1333: userScope.getComplibs());
1334:
1335: Set<ExtensionComplib> prjComplibs = getComplibsForProject(project);
1336: for (Iterator<ExtensionComplib> iter = result.iterator(); iter
1337: .hasNext();) {
1338: ExtensionComplib usrComplib = iter.next();
1339:
1340: // TODO Generalize this code to work with future EE versions
1341: if (isNonEe5Project
1342: && usrComplib.getCompLibManifest()
1343: .getEeSpecVersion() == EeSpecVersion.JAVA_EE_5) {
1344: // Cannot use a Java EE 5 complib in a non-EE 5 project
1345: iter.remove();
1346: continue;
1347: }
1348:
1349: URI ns = usrComplib.getIdentifier().getNamespaceUri();
1350: for (ExtensionComplib prjComplib : prjComplibs) {
1351: if (ns.equals(prjComplib.getIdentifier()
1352: .getNamespaceUri())) {
1353: iter.remove();
1354: }
1355: }
1356: }
1357: return new ArrayList<ExtensionComplib>(result);
1358: }
1359:
1360: public void addProjectComplib(Project project,
1361: ExtensionComplib newComplib) throws IOException,
1362: ComplibException {
1363: replaceProjectComplib(project, null, newComplib);
1364: }
1365:
1366: /**
1367: * Use to keep track of AddableComplib changes
1368: *
1369: * @param listener
1370: */
1371: public synchronized void addPropertyChangeListener(
1372: PropertyChangeListener listener) {
1373: pceListeners.addPropertyChangeListener(listener);
1374: }
1375:
1376: public synchronized void removePropertyChangeListener(
1377: PropertyChangeListener listener) {
1378: pceListeners.removePropertyChangeListener(listener);
1379: }
1380:
1381: /**
1382: * Notify listeners that targetScope has changed
1383: *
1384: * @param targetScope
1385: */
1386: private void fireAddableComplibsChanged(Scope targetScope) {
1387: pceListeners.firePropertyChange(ADDABLE_COMPLIBS, null,
1388: targetScope);
1389: }
1390:
1391: /**
1392: * Returns either null or a comma separated list of display names of currently opened rave
1393: * projects that use a particular complib. "Use" means that the complib has the same ComplibId
1394: * as a complib that is embedded in a project. A return value of null, means no projects use
1395: * that complib.
1396: *
1397: * @param complib
1398: * @return
1399: */
1400: public String getInUseProjectNames(ExtensionComplib complib) {
1401: StringBuffer buf = new StringBuffer();
1402: int i = 0;
1403: Set<Project> openRaveProjects = getOpenRaveProjects();
1404: for (Project project : openRaveProjects) {
1405: String displayName = ProjectUtils.getInformation(project)
1406: .getDisplayName();
1407:
1408: Scope scope;
1409: try {
1410: scope = Scope.getScopeForProject(project);
1411: } catch (IOException e) {
1412: IdeUtil.logWarning("Skipping project: " + displayName,
1413: e);
1414: continue;
1415: }
1416:
1417: if (scope.contains(complib)) {
1418: if (i > 0) {
1419: buf.append(", ");
1420: }
1421: buf.append(displayName);
1422: i++;
1423: }
1424: }
1425:
1426: return i == 0 ? null : buf.toString();
1427: }
1428:
1429: public Preferences getPreferences() {
1430: if (preferences == null) {
1431: preferences = NbPreferences
1432: .forModule(ComplibServiceProvider.class);
1433: }
1434: return preferences;
1435: }
1436:
1437: private static class SharedComplibState {
1438: /*
1439: * Preferences key = value format is:
1440: *
1441: * "sharedComplibs." + project-dir-full-path = CSV of dependent-project-base-names
1442: */
1443: private static final String SHARED_COMPLIBS = "sharedComplibs.";
1444:
1445: private static final SharedComplibState INSTANCE = new SharedComplibState();
1446:
1447: private Preferences prefs = NbPreferences
1448: .forModule(ComplibServiceProvider.class);
1449:
1450: /**
1451: * Maps Project-s to Set-s containing the SharedComplib-s for a project
1452: */
1453: private Map<Project, Set<SharedComplib>> map;
1454:
1455: private HashSet<SharedComplib> allSharedComplibs = new HashSet<SharedComplib>();
1456:
1457: public static SharedComplibState getInstance() {
1458: return INSTANCE;
1459: }
1460:
1461: private SharedComplibState() {
1462: map = new HashMap<Project, Set<SharedComplib>>();
1463: }
1464:
1465: public void addSharedComplib(Project project,
1466: SharedComplib complib) {
1467: allSharedComplibs.add(complib);
1468:
1469: Set<SharedComplib> complibs = map.get(project);
1470: if (complibs == null) {
1471: complibs = new HashSet<SharedComplib>();
1472: }
1473: complibs.add(complib);
1474: map.put(project, complibs);
1475: saveSharedComplibsForProject(project, complibs);
1476: }
1477:
1478: public Set<SharedComplib> getSharedComplibs(Project project) {
1479: Set<SharedComplib> complibs = map.get(project);
1480: if (complibs == null) {
1481: complibs = loadSharedComplibsForProject(project);
1482: allSharedComplibs.addAll(complibs);
1483: }
1484: return complibs;
1485: }
1486:
1487: public HashSet<SharedComplib> getAllSharedComplibs() {
1488: return allSharedComplibs;
1489: }
1490:
1491: public void removeSharedComplib(Project project,
1492: SharedComplib complibToRemove) {
1493: Set<SharedComplib> complibs = getSharedComplibs(project);
1494: if (complibs.remove(complibToRemove)) {
1495: // Remove from set of all shared complibs if no longer used
1496: boolean found = false;
1497: Collection<Set<SharedComplib>> allShared = map.values();
1498: for (Set<SharedComplib> set : allShared) {
1499: if (set.contains(complibToRemove)) {
1500: found = true;
1501: break;
1502: }
1503: }
1504:
1505: if (!found) {
1506: allSharedComplibs.remove(complibToRemove);
1507: }
1508:
1509: saveSharedComplibsForProject(project, complibs);
1510: }
1511: }
1512:
1513: /**
1514: * Factory method to create a SharedComplib from a NetBeans Project. Note that a particular
1515: * build structure is assumed.
1516: *
1517: * @param project
1518: * @return SharedComplib or throws exception
1519: * @throws ComplibException
1520: * @throws IOException
1521: */
1522: public SharedComplib createSharedComplibFromProject(
1523: Project project) throws ComplibException, IOException {
1524: FileObject complibRoot = projectToSharedComplibDir(project);
1525: if (complibRoot == null) {
1526: throw new ComplibException(
1527: "Shared component library project must have a built 'build/complib' directory.");
1528: }
1529:
1530: SharedComplib complib = new SharedComplib(FileUtil
1531: .toFile(complibRoot));
1532: return complib;
1533: }
1534:
1535: public FileObject projectToSharedComplibDir(Project project) {
1536: FileObject projectDirFo = project.getProjectDirectory();
1537:
1538: // XXX Warning assumes a particular build dir structure
1539: FileObject complibRoot = projectDirFo
1540: .getFileObject("build/complib");
1541: return complibRoot;
1542: }
1543:
1544: /**
1545: * Returns the Project directory corresponding to a SharedComplib. Assumes a particular
1546: * directory structure. Return null if there is a problem.
1547: *
1548: * @param complib
1549: * @return
1550: */
1551: public File sharedComplibDirToProjectDir(SharedComplib complib) {
1552: // XXX Assumes structure is projectDir/build/complib/
1553: File complibDir = complib.getDirectory();
1554: File parentFile = complibDir.getParentFile();
1555: if (parentFile == null) {
1556: // Somethings wrong so skip it
1557: return null;
1558: }
1559: parentFile = parentFile.getParentFile();
1560: if (parentFile == null) {
1561: // Somethings wrong so skip it
1562: return null;
1563: }
1564: return parentFile;
1565: }
1566:
1567: private Set<SharedComplib> loadSharedComplibsForProject(
1568: Project project) {
1569: HashSet<SharedComplib> complibs = new HashSet<SharedComplib>();
1570:
1571: String key = SHARED_COMPLIBS
1572: + project.getProjectDirectory().getPath();
1573:
1574: // Init from the persisted projects list
1575: String projectCSV = prefs.get(key, "");
1576: String[] projectBasenames = projectCSV.split(",");
1577: for (String projectName : projectBasenames) {
1578: Project[] openProjects = OpenProjects.getDefault()
1579: .getOpenProjects();
1580: for (Project iProject : openProjects) {
1581: if (iProject.getProjectDirectory().getNameExt()
1582: .equals(projectName)) {
1583: SharedComplib complib;
1584: try {
1585: complib = createSharedComplibFromProject(iProject);
1586: } catch (Exception e) {
1587: IdeUtil.logWarning(
1588: "Skipping bad shared complib", e);
1589: continue;
1590: }
1591: complibs.add(complib);
1592: break;
1593: }
1594: }
1595: }
1596: return complibs;
1597: }
1598:
1599: /**
1600: * @param project
1601: * @param complibs
1602: */
1603: private void saveSharedComplibsForProject(Project project,
1604: Set<SharedComplib> complibs) {
1605: String key = SHARED_COMPLIBS
1606: + project.getProjectDirectory().getPath();
1607:
1608: StringBuffer sb = new StringBuffer();
1609: for (SharedComplib complib : complibs) {
1610: File projectDir = sharedComplibDirToProjectDir(complib);
1611: if (projectDir == null) {
1612: // Skip if there is a problem
1613: continue;
1614: }
1615:
1616: if (sb.length() > 0) {
1617: sb.append(",");
1618: }
1619: sb.append(projectDir.getName());
1620: }
1621: prefs.put(key, sb.toString());
1622: }
1623: }
1624:
1625: private Project getActiveProject() throws ComplibException {
1626: Project activeProject = IdeUtil.getActiveProject();
1627: if (activeProject == null) {
1628: throw new ComplibException(
1629: "Error: No active project. Select one in designer.");
1630: }
1631: return activeProject;
1632: }
1633:
1634: public List<Project> getEligibleSharedComplibProjects() {
1635: ArrayList<Project> result = new ArrayList<Project>();
1636: Project[] openProjects = OpenProjects.getDefault()
1637: .getOpenProjects();
1638: SharedComplibState scs = SharedComplibState.getInstance();
1639: for (Project project : openProjects) {
1640: if (scs.projectToSharedComplibDir(project) != null) {
1641: result.add(project);
1642: }
1643: }
1644: return result;
1645: }
1646:
1647: /**
1648: * Attempt to add project as a shared complib project to the active project.
1649: *
1650: * @param shComplibProject
1651: * @throws IOException
1652: * @throws ComplibException
1653: */
1654: public void addSharedComplibProject(Project shComplibProject)
1655: throws ComplibException, IOException {
1656: Project activeProject = getActiveProject();
1657: SharedComplibState scs = SharedComplibState.getInstance();
1658: SharedComplib complib = scs
1659: .createSharedComplibFromProject(shComplibProject);
1660:
1661: // Only add to palette if it is not already there
1662: if (!scs.getAllSharedComplibs().contains(complib)) {
1663: try {
1664: addToPalette(complib);
1665: } catch (ComplibException e1) {
1666: // Rollback
1667: removeFromPaletteCategories(complib);
1668: throw e1;
1669: }
1670: }
1671:
1672: scs.addSharedComplib(activeProject, complib);
1673: addSharedComplibLibraryDefAndRefs(activeProject, complib);
1674: }
1675:
1676: public Set<SharedComplib> getSharedComplibsForActiveProject()
1677: throws ComplibException {
1678: Project activeProject = getActiveProject();
1679: return SharedComplibState.getInstance().getSharedComplibs(
1680: activeProject);
1681: }
1682:
1683: public void removeSharedComplibsFromActiveProject(
1684: Set<SharedComplib> complibsToRemove) throws IOException,
1685: ComplibException {
1686: Project activeProject = getActiveProject();
1687: SharedComplibState scs = SharedComplibState.getInstance();
1688: for (SharedComplib complib : complibsToRemove) {
1689: removeSharedComplibLibraryDefAndRefs(activeProject, complib);
1690:
1691: scs.removeSharedComplib(activeProject, complib);
1692:
1693: // Remove from palette only when no longer in use
1694: Set<SharedComplib> allShared = scs.getAllSharedComplibs();
1695: if (!allShared.contains(complib)) {
1696: removeFromPaletteCategories(complib);
1697: }
1698: }
1699: }
1700:
1701: public void refreshSharedComplibsForActiveProject()
1702: throws ComplibException {
1703: Set<SharedComplib> complibs = getSharedComplibsForActiveProject();
1704: for (SharedComplib complib : complibs) {
1705: removeFromPaletteCategories(complib);
1706: addToPalette(complib);
1707: }
1708: }
1709:
1710: /**
1711: * Add any needed library definition and references for a shared complib to a project.
1712: *
1713: * @param project
1714: * @param sharedComplib
1715: * @throws IOException
1716: */
1717: private void addSharedComplibLibraryDefAndRefs(Project project,
1718: SharedComplib sharedComplib) throws IOException {
1719: String localizingBundle = LibraryLocalizationBundle.class
1720: .getName();
1721:
1722: LibraryDescriptor libDescriptor = deriveSharedComplibLibraryName(sharedComplib);
1723: String libName = libDescriptor.getLibName();
1724:
1725: Library libDef = LibraryManager.getDefault()
1726: .getLibrary(libName);
1727: if (libDef != null) {
1728: // Assume the definition is correct, if not the user can manually
1729: // remove it and it will be recreated when the project is re-opened
1730: return;
1731: }
1732:
1733: // Use the name of the library as a key for the description
1734: LibraryLocalizationBundle.add(libName, libDescriptor
1735: .getDescription());
1736:
1737: List<URL> rtPath = fileListToUrlList(sharedComplib
1738: .getRuntimePath());
1739: List<URL> dtPath = fileListToUrlList(sharedComplib
1740: .getDesignTimePath());
1741: List<URL> javadocPath = fileListToUrlList(sharedComplib
1742: .getJavadocPath());
1743: List<URL> sourcePath = fileListToUrlList(sharedComplib
1744: .getSourcePath());
1745: libDef = JsfProjectUtils.createComponentLibrary(libName,
1746: libName, localizingBundle, rtPath, sourcePath,
1747: javadocPath, dtPath);
1748:
1749: // Create new compile-time and deploy Library Ref
1750: if (!JsfProjectUtils.addLibraryReferences(project,
1751: new Library[] { libDef })) {
1752: IdeUtil
1753: .logError("Failed to add compile-time library reference to project: "
1754: + libDef.getName());
1755: }
1756: }
1757:
1758: /**
1759: * Remove any existing NB Library Defs and Refs corresponding to a shared complib for a project
1760: *
1761: * @param project
1762: * @param sharedComplib
1763: * @throws IOException
1764: */
1765: private void removeSharedComplibLibraryDefAndRefs(Project project,
1766: SharedComplib sharedComplib) throws IOException {
1767: LibraryDescriptor libDescriptor = deriveSharedComplibLibraryName(sharedComplib);
1768: String libName = libDescriptor.getLibName();
1769:
1770: Library libDef = JsfProjectUtils.getProjectLibraryManager(
1771: project).getLibrary(libName);
1772: if (libDef != null) {
1773: // Existing definition so first remove any existing references
1774: JsfProjectUtils.removeLibraryReferences(project,
1775: new Library[] { libDef });
1776:
1777: JsfProjectUtils.removeLibrary(libName);
1778:
1779: // Cleanup bundle
1780: LibraryLocalizationBundle.remove(libName);
1781: }
1782: }
1783:
1784: private static class LibraryDescriptor {
1785: private String libName;
1786:
1787: private String description;
1788:
1789: public LibraryDescriptor(String libName, String description) {
1790: this .libName = libName;
1791: this .description = description;
1792: }
1793:
1794: public String getDescription() {
1795: return description;
1796: }
1797:
1798: public String getLibName() {
1799: return libName;
1800: }
1801:
1802: }
1803:
1804: /**
1805: * Derive name and description for global NB Library Defs for a shared component library.
1806: *
1807: * @param sharedComplib
1808: * @return
1809: */
1810: private LibraryDescriptor deriveSharedComplibLibraryName(
1811: SharedComplib sharedComplib) {
1812: String description = SharedComplibState.getInstance()
1813: .sharedComplibDirToProjectDir(sharedComplib).getName()
1814: + " Shared Component Library";
1815: String libName = IdeUtil.removeWhiteSpace(description);
1816: return new LibraryDescriptor(libName, description);
1817: }
1818:
1819: /**
1820: * Returns true iff the embedded project complib refs and defs need to be migrated.
1821: *
1822: * @param project
1823: * @return
1824: */
1825: private boolean needsMigration(Project project) {
1826: if (!JsfProjectUtils.isJsfProject(project)) {
1827: // No migration needed for non-visualweb projects
1828: return false;
1829: }
1830:
1831: // Check to see if the migration has already taken place
1832: FileObject projectDir = project.getProjectDirectory();
1833: Object migrationVersion = projectDir
1834: .getAttribute(ATTR_MIGRATION_VERSION);
1835: if (ProjectComplibVersion.NETBEANS_6.equals(migrationVersion)) {
1836: // Already migrated
1837: return false;
1838: }
1839:
1840: return true;
1841: }
1842:
1843: public List<String> getLibRefNamesToRemove(Project project) {
1844: if (!needsMigration(project)) {
1845: return Collections.emptyList();
1846: }
1847:
1848: List<String> libNames = new ArrayList<String>();
1849:
1850: Scope scope = null;
1851: try {
1852: scope = Scope.getScopeForProject(project);
1853: } catch (IOException e) {
1854: IdeUtil
1855: .logWarning(
1856: "Unable to obtain legacy library reference information for project: '"
1857: + project.getProjectDirectory()
1858: + "'. You may need to use Project Properties to remove broken references manually.",
1859: e);
1860: }
1861: Set<ExtensionComplib> projectComplibs = scope.getComplibs();
1862: String projectName = project.getProjectDirectory().getName();
1863: for (ExtensionComplib complib : projectComplibs) {
1864: /*
1865: * In NetBeans VWP 5.5.x or shortfin, there was only a single Lib Def with a
1866: * "design-time" volume but two separate Lib Refs that showed up in the Project
1867: * Properites UI as a node under "Libraries" w/o package checkbox and another under
1868: * "Packaging".
1869: */
1870: String libName = IdeUtil.removeWhiteSpace(projectName + "_"
1871: + complib.getDirectoryBaseName());
1872: libNames.add(libName);
1873:
1874: /*
1875: * In Creator, there were two pairs of Lib Def and Lib Refs, one for design-time and one
1876: * for runtime.
1877: */
1878: libName = IdeUtil.removeWhiteSpace(projectName
1879: + " Design-time " + complib.getTitle());
1880: libNames.add(libName);
1881:
1882: libName = IdeUtil.removeWhiteSpace(projectName
1883: + " Runtime " + complib.getTitle());
1884: libNames.add(libName);
1885: }
1886:
1887: return libNames;
1888: }
1889:
1890: public void initProjectComplibs(Project project) {
1891: try {
1892: Scope scope = Scope.getScopeForProject(project);
1893: Set<ExtensionComplib> projectComplibs = scope.getComplibs();
1894: ensureProjectComplibsAreInstalled(projectComplibs);
1895:
1896: // Init library refs and defs for each complib
1897: for (ExtensionComplib projectComplib : projectComplibs) {
1898: ExtensionComplib userComplib = userScope
1899: .getExistingComplib(projectComplib);
1900: if (userComplib != null) {
1901: addLibraryRefAndDef(project, userComplib);
1902: }
1903: }
1904: } catch (IOException e) {
1905: IdeUtil.logError(
1906: "Unable to initialize component libraries for project: "
1907: + project.getProjectDirectory(), e);
1908: }
1909:
1910: /*
1911: * This code is related to project complib migration. Note: there is some magic that happens
1912: * in web/project that is not obvious. According to issue #110040, the
1913: * BrokenLibraryRefFilter is called which removes legacy library refs from the project
1914: * before the broken library references dialog is invoked in web/project. Then,
1915: * ComplibProjectOpenedHook is called which will add any needed library refs and defs by
1916: * calling this method. We therefore note that the embedded project complib refs and defs
1917: * have been successfully migrated.
1918: */
1919: FileObject projectDir = project.getProjectDirectory();
1920: try {
1921: projectDir.setAttribute(ATTR_MIGRATION_VERSION,
1922: ProjectComplibVersion.NETBEANS_6);
1923: } catch (IOException e) {
1924: IdeUtil.logWarning(e);
1925: }
1926: }
1927:
1928: public void cleanUpProjectComplibs(Project project) {
1929: try {
1930: Scope.destroyScopeForProject(project);
1931: } catch (IOException e) {
1932: IdeUtil.logWarning(e);
1933: }
1934: }
1935:
1936: }
|