001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.apisupport.project.ui.customizer;
043:
044: import java.text.Collator;
045: import java.util.Comparator;
046: import java.util.HashSet;
047: import java.util.Iterator;
048: import java.util.Set;
049: import org.netbeans.modules.apisupport.project.universe.ModuleEntry;
050: import org.netbeans.spi.project.support.ant.PropertyUtils;
051: import org.openide.util.Utilities;
052:
053: /**
054: * Represents one module dependency. I.e. <em><dependency></em> element
055: * in module's <em>project.xml</em> or one token in the
056: * OpenIDE-Module-Module-Dependencies attribute of module manifest.
057: * <p>
058: * Natural ordering is based sequentially on the code name base of {@link
059: * ModuleEntry} this instance represents, release version, specification
060: * version, implementation dependency and compilation dependency. Two instances
061: * are equals only if all the mentioned are <code>equals</code>.
062: * </p>
063: *
064: * @author Martin Krauskopf
065: */
066: public final class ModuleDependency implements
067: Comparable<ModuleDependency> {
068:
069: // XXX refactor and use SpecificationVersion instead
070: private String releaseVersion;
071: private String specVersion;
072: /**
073: * Defer loading spec version until really needed since it can be expensive.
074: */
075: private static final String SPEC_VERSION_LAZY = "<lazy>"; // NOI18N
076: private boolean implDep;
077: private boolean compileDep;
078:
079: private ModuleEntry me;
080:
081: private Set<String> filterTokensNotFriend;
082: private Set<String> filterTokensFriend;
083:
084: public static final Comparator<ModuleDependency> LOCALIZED_NAME_COMPARATOR;
085: public static final Comparator<ModuleDependency> CNB_COMPARATOR;
086:
087: static {
088: LOCALIZED_NAME_COMPARATOR = new Comparator<ModuleDependency>() {
089: public int compare(ModuleDependency d1, ModuleDependency d2) {
090: ModuleEntry me1 = d1.getModuleEntry();
091: ModuleEntry me2 = d2.getModuleEntry();
092: int result = Collator.getInstance().compare(
093: me1.getLocalizedName(), me2.getLocalizedName());
094: return result != 0 ? result : me1.getCodeNameBase()
095: .compareTo(me2.getCodeNameBase());
096: }
097: };
098: CNB_COMPARATOR = new Comparator<ModuleDependency>() {
099: public int compare(ModuleDependency d1, ModuleDependency d2) {
100: return d1.getCodeNameBase().compareTo(
101: d2.getCodeNameBase());
102: }
103: };
104: }
105:
106: /**
107: * Creates a new instance based on the given entry. The instance will be
108: * initialized with given entry's release and specification versions.
109: * Compile dependency is set to true by default, implementation version to
110: * false.
111: */
112: public ModuleDependency(ModuleEntry me) {
113: this (me, me.getReleaseVersion(), SPEC_VERSION_LAZY, me
114: .getPublicPackages().length > 0, false);
115: }
116:
117: public ModuleDependency(ModuleEntry me, String releaseVersion,
118: String specVersion, boolean compileDep, boolean implDep) {
119: this .me = me;
120:
121: // set versions to null if contain the same value as the given entry
122: this .compileDep = compileDep;
123: this .implDep = implDep;
124: this .releaseVersion = releaseVersion;
125: this .specVersion = specVersion;
126: }
127:
128: /**
129: * Get the <b>major release version</b>.
130: * @return <code>null</code> for none or the version.
131: */
132: public String getReleaseVersion() {
133: return releaseVersion;
134: }
135:
136: public String getSpecificationVersion() {
137: if (specVersion == SPEC_VERSION_LAZY) {
138: specVersion = me.getSpecificationVersion();
139: }
140: return specVersion;
141: }
142:
143: public ModuleEntry getModuleEntry() {
144: return me;
145: }
146:
147: private String getCodeNameBase() {
148: return getModuleEntry().getCodeNameBase();
149: }
150:
151: public int compareTo(ModuleDependency other) {
152: int result = getCodeNameBase().compareTo(
153: other.getCodeNameBase());
154: if (result != 0) {
155: return result;
156: }
157:
158: // XXX this is not exact since we should use SpecificationVersion
159: // instead of String. In this way are not using Dewey-decimal comparison.
160: String relVersion = other.getReleaseVersion();
161: result = getReleaseVersion() == null // release versions may be null
162: ? (relVersion == null ? 0 : -1)
163: : (relVersion == null ? 1 : getReleaseVersion()
164: .compareTo(relVersion));
165: if (result != 0) {
166: return result;
167: }
168:
169: // do not force resolution of SPEC_VERSION_LAZY for comparisons, unnecessary
170: if (specVersion != SPEC_VERSION_LAZY
171: || other.specVersion != SPEC_VERSION_LAZY) {
172: String otherSpec = other.getSpecificationVersion();
173: String spec = getSpecificationVersion();
174: result = spec == null // spec versions may be null
175: ? (otherSpec == null ? 0 : -1)
176: : (otherSpec == null ? 1 : spec
177: .compareTo(otherSpec));
178: if (result != 0) {
179: return result;
180: }
181: }
182:
183: result = hasImplementationDepedendency() == other
184: .hasImplementationDepedendency() ? 0 : (implDep ? 1
185: : -1);
186: if (result != 0) {
187: return result;
188: }
189:
190: result = hasCompileDependency() == other.hasCompileDependency() ? 0
191: : (compileDep ? 1 : -1);
192: return result;
193: }
194:
195: public boolean equals(Object o) {
196: if (o instanceof ModuleDependency) {
197: ModuleDependency other = (ModuleDependency) o;
198: return getCodeNameBase().equals(other.getCodeNameBase())
199: && Utilities.compareObjects(getReleaseVersion(),
200: other.getReleaseVersion())
201: && ((specVersion == SPEC_VERSION_LAZY && other.specVersion == SPEC_VERSION_LAZY) || Utilities
202: .compareObjects(getSpecificationVersion(),
203: other.getSpecificationVersion()))
204: && (hasImplementationDepedendency() == other
205: .hasImplementationDepedendency())
206: && (hasCompileDependency() == other
207: .hasCompileDependency());
208: } else {
209: return false;
210: }
211: }
212:
213: public int hashCode() {
214: // specifically do not hash spec version
215: return getCodeNameBase().hashCode();
216: }
217:
218: public boolean hasCompileDependency() {
219: return compileDep;
220: }
221:
222: public boolean hasImplementationDepedendency() {
223: return implDep;
224: }
225:
226: /**
227: * Return a set of tokens that can be used to search for this dependency.
228: * Per UI spec, includes lower-case versions of:
229: * <ol>
230: * <li>the code name base
231: * <li>the localized display name
232: * <li> the full path to the module JAR or any Class-Path extension
233: * <li> the fully-qualified class name (use . for inner classes) of any class
234: * contained in the module JAR or any Class-Path extension which is in an package
235: * which would be made available to the depending module when using a specification version dependency
236: * </ol>
237: * Note that the last item means that this can behave differently according to the depending
238: * module (according to whether or not it would be listed as a friend).
239: * @param dependingModuleCNB the CNB of the module depending on this one
240: */
241: Set<String> getFilterTokens(String dependingModuleCNB) {
242: boolean friend = me.isDeclaredAsFriend(dependingModuleCNB);
243: Set<String> filterTokens = friend ? filterTokensFriend
244: : filterTokensNotFriend;
245: if (filterTokens == null) {
246: filterTokens = new HashSet<String>();
247: filterTokens.add(me.getCodeNameBase());
248: filterTokens.add(me.getLocalizedName());
249: filterTokens.add(me.getJarLocation().getAbsolutePath());
250: String[] cpext = PropertyUtils.tokenizePath(me
251: .getClassPathExtensions());
252: for (int i = 0; i < cpext.length; i++) {
253: filterTokens.add(cpext[i]);
254: }
255: if (friend) {
256: Iterator it = me.getPublicClassNames().iterator();
257: while (it.hasNext()) {
258: String clazz = (String) it.next();
259: filterTokens.add(clazz.replace('$', '.'));
260: }
261: }
262: if (friend) {
263: filterTokensFriend = filterTokens;
264: } else {
265: filterTokensNotFriend = filterTokens;
266: }
267: }
268: return filterTokens;
269: }
270:
271: public String toString() {
272: return "ModuleDependency[me: " + getModuleEntry() + // NOI18N
273: ", relVer: " + getReleaseVersion() + // NOI18N
274: ", specVer: " + getSpecificationVersion() + // NOI18N
275: ", implDep: " + hasImplementationDepedendency() + // NOI18N
276: ", compDep: " + hasCompileDependency() + // NOI18N
277: "]"; // NOI18N
278: }
279: }
|