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.uml.core.metamodel.structure;
043:
044: import org.netbeans.modules.uml.core.support.IAssociatedProjectSourceRoots;
045: import java.io.File;
046:
047: import org.dom4j.Document;
048: import org.dom4j.Node;
049:
050: import org.netbeans.modules.uml.core.coreapplication.ICoreProduct;
051: import org.netbeans.modules.uml.core.eventframework.EventDispatchNameKeeper;
052: import org.netbeans.modules.uml.core.eventframework.EventDispatchRetriever;
053: import org.netbeans.modules.uml.core.metamodel.core.foundation.ElementCollector;
054: import org.netbeans.modules.uml.core.metamodel.core.foundation.ElementConnector;
055: import org.netbeans.modules.uml.core.metamodel.core.foundation.IBackPointer;
056: import org.netbeans.modules.uml.core.metamodel.core.foundation.INamedElement;
057: import org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure.Classifier;
058: import org.netbeans.modules.uml.core.support.umlsupport.PathManip;
059: import org.netbeans.modules.uml.core.support.umlsupport.ProductRetriever;
060: import org.netbeans.modules.uml.core.support.umlsupport.StringUtilities;
061: import org.netbeans.modules.uml.core.support.umlutils.ETArrayList;
062: import org.netbeans.modules.uml.core.support.umlutils.ETList;
063: import org.netbeans.modules.uml.core.workspacemanagement.IWSProject;
064: import org.netbeans.modules.uml.core.workspacemanagement.IWorkspace;
065:
066: public class Artifact extends Classifier implements IArtifact {
067:
068: /**
069: *
070: */
071: public Artifact() {
072: super ();
073: }
074:
075: public IDeploymentSpecification getContent() {
076: ElementCollector<IDeploymentSpecification> collector = new ElementCollector<IDeploymentSpecification>();
077: return collector.retrieveSingleElementWithAttrID(this ,
078: "content", IDeploymentSpecification.class);
079: }
080:
081: public void setContent(IDeploymentSpecification pSpec) {
082: final IDeploymentSpecification spec = pSpec;
083: new ElementConnector<IArtifact>().setSingleElementAndConnect(
084: this , spec, "content",
085: new IBackPointer<IDeploymentSpecification>() {
086: public void execute(IDeploymentSpecification obj) {
087: obj.addDeploymentDescriptor(Artifact.this );
088: }
089: }, new IBackPointer<IDeploymentSpecification>() {
090: public void execute(IDeploymentSpecification obj) {
091: obj.removeDeploymentDescriptor(Artifact.this );
092: }
093: });
094: }
095:
096: public ETList<IDeployment> getDeployments() {
097: ElementCollector<IDeployment> collector = new ElementCollector<IDeployment>();
098: return collector.retrieveElementCollectionWithAttrIDs(this ,
099: "deployment", IDeployment.class);
100: }
101:
102: public void removeDeployment(IDeployment deployment) {
103: final IDeployment dep = deployment;
104: new ElementConnector<IArtifact>().removeByID(this , dep,
105: "deployment", new IBackPointer<IArtifact>() {
106: public void execute(IArtifact obj) {
107: dep.removeDeployedArtifact(obj);
108: }
109: });
110: }
111:
112: public void addDeployment(IDeployment deployment) {
113: final IDeployment dep = deployment;
114: new ElementConnector<IArtifact>().addChildAndConnect(this ,
115: true, "deployment", "deployment", dep,
116: new IBackPointer<IArtifact>() {
117: public void execute(IArtifact obj) {
118: dep.removeDeployedArtifact(obj);
119: }
120: });
121: }
122:
123: public ETList<INamedElement> getImplementedElements() {
124: ElementCollector<INamedElement> collector = new ElementCollector<INamedElement>();
125: return collector.retrieveElementCollectionWithAttrIDs(this ,
126: "implementedElement", INamedElement.class);
127: }
128:
129: public void removeImplementedElement(INamedElement comp) {
130: removeElementByID(comp, "implementedElement");
131: }
132:
133: public void addImplementedElement(INamedElement comp) {
134: addElementByID(comp, "implementedElement");
135: }
136:
137: /**
138: * Establishes the appropriate XML elements for this UML type.
139: *
140: * [in] The document where this element will reside
141: * [in] The element's parent node.
142: */
143: public void establishNodePresence(Document doc, Node parent) {
144: buildNodePresence("UML:Artifact", doc, parent);
145: }
146:
147: /**
148: * The absolute path to the associated file.
149: */
150: public String getFileName() {
151: String absolutePath = null;
152: String relPath = getAttributeValue("sourcefile");
153: if (relPath != null && relPath.length() > 0) {
154: String baseDir = getBaseDir();
155: absolutePath = retrieveAbsolutePath(baseDir, relPath);
156: }
157: return absolutePath;
158: }
159:
160: /**
161: * The absolute path to the associated file.
162: */
163: public void setFileName(String newVal) {
164: // this is different than in c++ because in c++ if we are setting the filename to an empty string
165: // we get through this method but an uncaught exception happens in StructureEventDispatcherImpl::FireArtifactFileNamePreModified
166: // which causes the process to stop, so the firePreFileNameChange returns false and it is done.
167: // In jUML, this is not the case, so retrieveRelativePath returns "\." which is also wrong, but
168: // to be consistent with c++, we will not allow the user to blank out the file name of an artifact.
169: if (newVal != null && newVal.length() > 0) {
170: if (firePreFileNameChange(newVal)) {
171: String relPath = retrieveRelativePath(newVal);
172: String oldFileName = getFileName();
173: setAttributeValue("sourcefile", relPath);
174: fireFileNameChange(oldFileName);
175: }
176: }
177: }
178:
179: /**
180: * The source file artifact's base directory.
181: */
182: protected String getBaseDir() {
183: String baseDir = null;
184: IProject proj = getProject();
185: if (proj != null) {
186: baseDir = proj.getBaseDirectory();
187: // ICoreProduct product = ProductRetriever.retrieveProduct();
188: // if (product != null)
189: // {
190: // IWorkspace ws = product.getCurrentWorkspace();
191: // if (ws != null)
192: // {
193: // String projName = proj.getName();
194: // IWSProject wsProj = ws.getWSProjectByName(projName);
195: // if (wsProj != null)
196: // {
197: // baseDir = wsProj.getBaseDirectory();
198: // }
199: // }
200: // }
201: }
202: return baseDir;
203: }
204:
205: /**
206: *
207: * Retrieves the absolute path of relative.
208: *
209: * @param base[in] The location to root the path from
210: * @param relative[in] The relative path to root. If relative is not a relative path,
211: * its value will simply be returned
212: */
213: public String retrieveAbsolutePath(String base, String relative) {
214: String absolutePath = null;
215:
216: // // In NetBeans 4.1 we now associate a UML project to a NetBeans Project.
217: // // Since a NetBeans project can have multiple source roots we first try
218: // // convert the filename by using the soruce roots.
219: // IProject project = getProject();
220: // if(project != null)
221: // {
222: // IAssociatedProjectSourceRoots roots = project.getAssociatedProjectSourceRoots();
223: // if(roots != null)
224: // {
225: // absolutePath = roots.createAbsolutePath(relative);
226: // }
227: // }
228:
229: // If we failed to convert the filename by the source roots approach
230: // try to use the old way. (Backward Compatible)
231: if ((absolutePath == null) || (absolutePath.length() == 0)) {
232: File file = new File(relative);
233: if (file != null && !file.isAbsolute()) {
234: if (base != null && relative != null) {
235: absolutePath = PathManip
236: .retrieveSourceAbsolutePath(getProject(),
237: relative, base);
238: }
239: } else if (relative != null) {
240: absolutePath = relative;
241: }
242: }
243: return absolutePath;
244: }
245:
246: /**
247: *
248: * Retrieves the relative path between newFile and curFile.
249: *
250: * @param newFile[in] The new file we are trying to get a relative path to
251: *
252: * @return The relative path, else "" on error.
253: *
254: */
255: public String retrieveRelativePath(String newFileName) {
256: String baseDir = getBaseDir();
257:
258: String retVal = "";
259:
260: // // In NetBeans 4.1 we now associate a UML project to a NetBeans Project.
261: // // Since a NetBeans project can have multiple source roots we first try
262: // // convert the filename by using the soruce roots.
263: // IProject project = getProject();
264: // if(project != null)
265: // {
266: // IAssociatedProjectSourceRoots roots = project.getAssociatedProjectSourceRoots();
267: // if(roots != null)
268: // {
269: // retVal = roots.createRelativePath(newFileName);
270: // }
271: // }
272:
273: // If we still did not have a relative path then use the old mechanism.
274: if (retVal.length() == 0) {
275: retVal = PathManip.retrieveSourceRelativePath(getProject(),
276: newFileName, baseDir);
277: }
278: return retVal;
279: }
280:
281: /**
282: * This operation calculates the artifact's base directory based on:
283: * - the owning classifier's fully qualified name
284: * - the path to the file
285: *
286: * For some examples:
287: *
288: * sourceFile qualifiedName baseDirectory
289: * ---------------------------------------------------------------------------------------
290: * C:\a\b\c\D.java D C:\a\b\c
291: * C:\a\b\c\D.java c.D C:\a\b
292: * C:\a\b\c\D.java b.c.D C:\a
293: * C:\a\b\c\D.java a.b.c.D C:\
294: * C:\b\c\D.java a.b.c.D ERROR - too few directories
295: * C:\a\b\c\D.java a.c.D ERROR - bad match (a vs. b)
296: *
297: * @param sourceFile[in] the name of the Classifier's source file artifact
298: * @param qualifiedName[in] the qualified name of the Classifier
299: * @param baseDirectory[out] the base directory or empty string if an error occurred.
300: */
301: public String getBaseDir(String sourceFile, String qualifiedName) {
302: ETList<String> qualifiedNames = StringUtilities
303: .splitOnDelimiter(qualifiedName, "::");
304:
305: if (qualifiedNames.size() == 0)
306: return sourceFile;
307:
308: File f = new File(sourceFile);
309: for (int i = qualifiedNames.size() - 1; i >= 0 && f != null; --i) {
310: String segment = qualifiedNames.get(i);
311: String fs = f.getName();
312: if (fs.indexOf(".") != -1)
313: fs = StringUtilities.getFileName(fs);
314: if (!segment.equals(fs))
315: break;
316: f = f.getParentFile();
317: }
318:
319: return f != null ? f.toString() : null;
320:
321: // String baseDirectory = null;
322: // if ( (sourceFile != null && sourceFile.length() > 0) &&
323: // (qualifiedName != null && qualifiedName.length() > 0) )
324: // {
325: // String[] srcTokens = sourceFile.split("\\\\");
326: // String[] qualifiedTokens = qualifiedName.split("\\.");
327: //
328: // //Sending only the directory
329: // int srcTokensLengthMinus = srcTokens.length - qualifiedTokens.length;
330: // String[] retDir = new String[srcTokensLengthMinus];
331: // for(int i=0;i<srcTokensLengthMinus;i++)
332: // {
333: // retDir[i] = srcTokens[i];
334: // }
335: //
336: // boolean done = false;
337: // int srcPtr = srcTokens.length - 1;
338: // int quaPtr = qualifiedTokens.length - 1;
339: //
340: // while (!done)
341: // {
342: // if (srcPtr == 0)
343: // {
344: // // this is bad. It means that our fully qualified name had more tokens
345: // // in it than tokens in the path to the source file.
346: // // We can't return a base directory.
347: // done = true;
348: // }
349: // else if (quaPtr == 0)
350: // {
351: // // running out of qualified name tokens before running
352: // // out of source file tokens (i.e directories)
353: // // is acceptable.
354: // baseDirectory = joinStrs(retDir,"\\");
355: // done = true;
356: // }
357: // else
358: // {
359: // //save the last token from each of the token lists
360: // String dirToken = srcTokens[srcPtr--];
361: // String nameToken = qualifiedTokens[quaPtr--];
362: // int k = dirToken.indexOf('.');
363: // if ( k != -1)
364: // {
365: // dirToken = dirToken.substring(0,k);
366: // }
367: // if (dirToken.compareToIgnoreCase(nameToken) != 0)
368: // {
369: // // uh-oh, the directory token did not match the qualified name token.
370: // // This means that there is some disagreement between what the class'
371: // //package structure looks like and what the artifact's
372: // // path looks like.
373: // done = true;
374: // }
375: // }
376: // }
377: // }
378: // return baseDirectory;
379: }
380:
381: private String joinStrs(String[] tokens, String toJoin) {
382: StringBuffer buf = new StringBuffer();
383: for (int i = 0; i < tokens.length; i++) {
384: buf.append(tokens[i]);
385: //don't add '\' at the end of the dir.
386: if (i != tokens.length - 1)
387: buf.append(toJoin);
388: }
389: return buf.toString();
390: }
391:
392: public boolean firePreFileNameChange(String newFileName) {
393: boolean proceed = false;
394: IStructureEventDispatcher dispatcher = getStructureEventDispatcher();
395: if (dispatcher != null) {
396: proceed = dispatcher.fireArtifactFileNamePreModified(this ,
397: newFileName, null);
398: }
399: return proceed;
400: }
401:
402: public void fireFileNameChange(String oldFileName) {
403: IStructureEventDispatcher dispatcher = getStructureEventDispatcher();
404: if (dispatcher != null) {
405: dispatcher.fireArtifactFileNameModified(this , oldFileName,
406: null);
407: }
408: }
409:
410: private IStructureEventDispatcher getStructureEventDispatcher() {
411: EventDispatchRetriever retriever = EventDispatchRetriever
412: .instance();
413: return (IStructureEventDispatcher) retriever
414: .getDispatcher(EventDispatchNameKeeper.structure());
415:
416: }
417: }
|