This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 52886
Collapse All | Expand All

(-)ant/project/apichanges.xml (+37 lines)
Lines 96-101 Link Here
96
            <issue number="51468"/>
96
            <issue number="51468"/>
97
        </change>
97
        </change>
98
98
99
        <change>
100
            <api name="general"/>
101
            <summary>AntArtifact enhancements</summary>
102
            <version major="1" minor="4"/>
103
            <date day="3" month="1" year="2005"/>
104
            <author login="dkonecny"/>
105
            <compatibility addition="yes" deprecation="yes">
106
                <p>
107
                    New schema was defined, but upgrade from old schema to new
108
                    one is realized only after some new features are used. If
109
                    project's artifact does not define any properties for 
110
                    artifact nor produce multiple outputs and Ant script lies
111
                    under the project's directory then old schema is
112
                    always used. Once project start using some of these new 
113
                    features the schema will be upgraded automatically to new
114
                    version. This affects any project type which is using 
115
                    ant/project module.
116
                </p>
117
            </compatibility>
118
            <description>
119
                <p>
120
                    Several enhancements of AntArtifact were implemented:
121
                </p>
122
                <ol>
123
                    <li>execution of artifact's target can be customized by properties</li>
124
                    <li>artifact can produce several build outputs</li>
125
                    <li>Ant script path is not persisted as URI, but string possibly containing Ant properties</li>
126
                </ol>
127
                <p>
128
                    ReferenceHelper class was simplified as part of the implementation.
129
                </p>
130
            </description>
131
            <issue number="47788"/>
132
            <issue number="50484"/>
133
            <issue number="50092"/>
134
        </change>
135
99
    </changes>
136
    </changes>
100
137
101
    <!-- Now the surrounding HTML text and document structure: -->
138
    <!-- Now the surrounding HTML text and document structure: -->
(-)ant/project/src/org/netbeans/api/project/ant/AntArtifact.java (-9 / +75 lines)
Lines 17-22 Link Here
17
import java.net.MalformedURLException;
17
import java.net.MalformedURLException;
18
import java.net.URI;
18
import java.net.URI;
19
import java.net.URL;
19
import java.net.URL;
20
import java.util.ArrayList;
21
import java.util.Properties;
20
import org.netbeans.api.project.FileOwnerQuery;
22
import org.netbeans.api.project.FileOwnerQuery;
21
import org.netbeans.api.project.Project;
23
import org.netbeans.api.project.Project;
22
import org.openide.ErrorManager;
24
import org.openide.ErrorManager;
Lines 38-43 Link Here
38
 */
40
 */
39
public abstract class AntArtifact {
41
public abstract class AntArtifact {
40
    
42
    
43
    private final Properties PROPS = new Properties();
44
    
41
    /**
45
    /**
42
     * Empty constructor for use from subclasses.
46
     * Empty constructor for use from subclasses.
43
     */
47
     */
Lines 86-96 Link Here
86
    
90
    
87
    /**
91
    /**
88
     * Get the location of the build artifact relative to the Ant script.
92
     * Get the location of the build artifact relative to the Ant script.
89
     * For example, <samp>dist/mylib.jar</samp>.
93
     * See {@link #getArtifactLocations}.
90
     * @return a URI to the build artifact, resolved relative to {@link #getScriptLocation};
94
     * @return a URI to the build artifact, resolved relative to {@link #getScriptLocation};
91
     *         may be either relative, or an absolute <code>file</code>-protocol URI
95
     *         may be either relative, or an absolute <code>file</code>-protocol URI
96
     * @deprecated use {@link #getArtifactLocations} instead
97
     */
98
    public URI getArtifactLocation() {
99
        // XXX: diagnostic thread dump - this method should not be called anymore
100
        Thread.dumpStack();
101
        return getArtifactLocations()[0];
102
    }
103
104
    /**
105
     * Get the locations of the build artifacts relative to the Ant script.
106
     * For example, <samp>dist/mylib.jar</samp>. The method is not defined 
107
     * as abstract only for backward compatibility reasons. It has to be 
108
     * always overriden. The order is important and should stay the same
109
     * unless the artifact was changed.
110
     * @return an array of URIs to the build artifacts, resolved relative to {@link #getScriptLocation};
111
     *         may be either relative, or an absolute <code>file</code>-protocol URI
112
     * @since X.XX
92
     */
113
     */
93
    public abstract URI getArtifactLocation();
114
    public URI[] getArtifactLocations() {
115
        // XXX: diagnostic thread dump - this method must be always overriden
116
        Thread.dumpStack();
117
        return new URI[]{getArtifactLocation()};
118
    }
94
119
95
    /**
120
    /**
96
     * Returns identifier of the AntArtifact which must be <strong>unique within
121
     * Returns identifier of the AntArtifact which must be <strong>unique within
Lines 104-125 Link Here
104
129
105
    /**
130
    /**
106
     * Convenience method to find the actual artifact, if it currently exists.
131
     * Convenience method to find the actual artifact, if it currently exists.
107
     * Uses {@link #getScriptFile} or {@link #getScriptLocation} and resolves {@link #getArtifactLocation} from it.
132
     * See {@link #getArtifactFiles}.
108
     * Note that a project which has been cleaned more recently than it has been built
109
     * will generally not have the build artifact on disk and so this call may easily
110
     * return null. If you do not rely on the actual presence of the file but just need to
111
     * refer to it abstractly, use {@link #getArtifactLocation} instead.
112
     * @return the artifact file on disk, or null if it could not be found
133
     * @return the artifact file on disk, or null if it could not be found
134
     * @deprecated use {@link #getArtifactFiles} instead
113
     */
135
     */
114
    public final FileObject getArtifactFile() {
136
    public final FileObject getArtifactFile() {
115
        URI artifactLocation = getArtifactLocation();
137
        // XXX: diagnostic thread dump - do not call this method
138
        Thread.dumpStack();
139
        FileObject fos[] = getArtifactFiles();
140
        if (fos.length > 0) {
141
            return fos[0];
142
        } else {
143
            return null;
144
        }
145
    }
146
    
147
    private FileObject getArtifactFile(URI artifactLocation) {
116
        assert !artifactLocation.isAbsolute() ||
148
        assert !artifactLocation.isAbsolute() ||
117
            (!artifactLocation.isOpaque() && "file".equals(artifactLocation.getScheme())) // NOI18N
149
            (!artifactLocation.isOpaque() && "file".equals(artifactLocation.getScheme())) // NOI18N
118
            : artifactLocation;
150
            : artifactLocation;
119
        URL artifact;
151
        URL artifact;
120
        try {
152
        try {
121
            // XXX this should probably use something in PropertyUtils?
153
            // XXX this should probably use something in PropertyUtils?
122
            artifact = getScriptLocation().toURI().resolve(getArtifactLocation()).normalize().toURL();
154
            artifact = getScriptLocation().toURI().resolve(artifactLocation).normalize().toURL();
123
        } catch (MalformedURLException e) {
155
        } catch (MalformedURLException e) {
124
            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
156
            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
125
            return null;
157
            return null;
Lines 134-139 Link Here
134
    }
166
    }
135
    
167
    
136
    /**
168
    /**
169
     * Convenience method to find the actual artifacts, if they currently exist.
170
     * Uses {@link #getScriptFile} or {@link #getScriptLocation} and resolves {@link #getArtifactLocations} from it.
171
     * Note that a project which has been cleaned more recently than it has been built
172
     * will generally not have the build artifacts on disk and so this call may easily
173
     * return empty array. If you do not rely on the actual presence of the file but just need to
174
     * refer to it abstractly, use {@link #getArtifactLocations} instead.
175
     * @return the artifact files which exist on disk, or empty array if none could be found
176
     * @since X.XX
177
     */
178
    public final FileObject[] getArtifactFiles() {
179
        URI artifactLocations[] = getArtifactLocations();
180
        ArrayList l = new ArrayList();
181
        for (int i=0; i<artifactLocations.length; i++) {
182
            FileObject fo = getArtifactFile(artifactLocations[i]);
183
            if (fo != null) {
184
                l.add(fo);
185
            }
186
        }
187
        return (FileObject[])l.toArray(new FileObject[l.size()]);
188
    }
189
    
190
    /**
137
     * Convenience method to find the actual script file, if it currently exists.
191
     * Convenience method to find the actual script file, if it currently exists.
138
     * Uses {@link #getScriptLocation}.
192
     * Uses {@link #getScriptLocation}.
139
     * The script must exist on disk (Ant cannot run scripts from NetBeans
193
     * The script must exist on disk (Ant cannot run scripts from NetBeans
Lines 154-159 Link Here
154
     */
208
     */
155
    public Project getProject() {
209
    public Project getProject() {
156
        return FileOwnerQuery.getOwner(getScriptLocation().toURI());
210
        return FileOwnerQuery.getOwner(getScriptLocation().toURI());
211
    }
212
213
    /**
214
     * Optional properties which are used for Ant target execution. Only
215
     * properties necessary for customization of Ant target execution should
216
     * be used. These properties are stored in project.xml of project using 
217
     * this artifact so care should be taken in defining what properties
218
     * are used, e.g. never use absolute path like values
219
     * @since X.XX
220
     */
221
    public Properties getProperties() {
222
        return PROPS;
157
    }
223
    }
158
    
224
    
159
}
225
}
(-)ant/project/src/org/netbeans/modules/project/ant/ant-project-references2.xsd (+55 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
                Sun Public License Notice
4
5
The contents of this file are subject to the Sun Public License
6
Version 1.0 (the "License"). You may not use this file except in
7
compliance with the License. A copy of the License is available at
8
http://www.sun.com/
9
10
The Original Code is NetBeans. The Initial Developer of the Original
11
Code is Sun Microsystems, Inc. Portions Copyright 1997-2005 Sun
12
Microsystems, Inc. All Rights Reserved.
13
-->
14
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
15
            targetNamespace="http://www.netbeans.org/ns/ant-project-references/2"
16
            xmlns="http://www.netbeans.org/ns/ant-project-references/2"
17
            elementFormDefault="qualified">
18
    <xsd:element name="references">
19
        <xsd:complexType>
20
            <xsd:sequence>
21
                <xsd:element name="reference" minOccurs="0" maxOccurs="unbounded">
22
                    <xsd:complexType>
23
                        <xsd:sequence>
24
                            <xsd:element name="foreign-project" type="xsd:NCName"/>
25
                            <xsd:element name="artifact-type" type="xsd:NCName"/>
26
                            <!-- semantics of script field changed in /2 version (#50092) -->
27
                            <xsd:element name="script" type="xsd:NCName"/>
28
                            <xsd:element name="target" type="xsd:NCName"/>
29
                            <xsd:element name="clean-target" type="xsd:NCName"/>
30
                            <xsd:element name="id" type="xsd:NCName"/>
31
                            <xsd:element name="properties" minOccurs="0" type="properties"/>
32
                        </xsd:sequence>
33
                    </xsd:complexType>
34
                </xsd:element>
35
            </xsd:sequence>
36
        </xsd:complexType>
37
    </xsd:element>
38
39
    <xsd:complexType name="properties">
40
        <xsd:sequence>
41
            <xsd:choice minOccurs="0" maxOccurs="unbounded">
42
                <xsd:element name="property" type="property-definition"/>
43
            </xsd:choice>
44
        </xsd:sequence>
45
    </xsd:complexType>
46
    
47
    <xsd:complexType name="property-definition">
48
        <xsd:simpleContent>
49
            <xsd:extension base="substitutable-text">
50
                <xsd:attribute name="name" type="xsd:NMTOKEN"/>
51
            </xsd:extension>
52
        </xsd:simpleContent>
53
    </xsd:complexType>
54
        
55
</xsd:schema>
(-)ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java (-136 / +523 lines)
Lines 19-27 Link Here
19
import java.net.URISyntaxException;
19
import java.net.URISyntaxException;
20
import java.util.ArrayList;
20
import java.util.ArrayList;
21
import java.util.Arrays;
21
import java.util.Arrays;
22
import java.util.Collections;
22
import java.util.Iterator;
23
import java.util.Iterator;
23
import java.util.List;
24
import java.util.List;
24
import java.util.Map;
25
import java.util.Map;
26
import java.util.Properties;
25
import java.util.regex.Matcher;
27
import java.util.regex.Matcher;
26
import java.util.regex.Pattern;
28
import java.util.regex.Pattern;
27
import org.netbeans.api.project.Project;
29
import org.netbeans.api.project.Project;
Lines 84-89 Link Here
84
     */
86
     */
85
    static final String REFS_NS = "http://www.netbeans.org/ns/ant-project-references/1"; // NOI18N
87
    static final String REFS_NS = "http://www.netbeans.org/ns/ant-project-references/1"; // NOI18N
86
    
88
    
89
    /**
90
     * Newer version of {@link #REFS_NS} supporting Properties and with changed semantics of <script>.
91
     */
92
    static final String REFS_NS2 = "http://www.netbeans.org/ns/ant-project-references/2"; // NOI18N
93
    
87
    private final AntProjectHelper h;
94
    private final AntProjectHelper h;
88
    final PropertyEvaluator eval;
95
    final PropertyEvaluator eval;
89
    private final AuxiliaryConfiguration aux;
96
    private final AuxiliaryConfiguration aux;
Lines 116-128 Link Here
116
123
117
    /**
124
    /**
118
     * Load <references> from project.xml.
125
     * Load <references> from project.xml.
119
     * @param create if true, create an empty element if it was missing, else leave as null
126
     * @return can return null if there are no references stored yet
120
     */
127
     */
121
    private Element loadReferences(boolean create) {
128
    private Element loadReferences() {
122
        assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess();
129
        assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess();
123
        Element references = aux.getConfigurationFragment(REFS_NAME, REFS_NS, true);
130
        Element references = aux.getConfigurationFragment(REFS_NAME, REFS_NS2, true);
124
        if (references == null && create) {
131
        if (references == null) {
125
            references = XMLUtil.createDocument("ignore", null, null, null).createElementNS(REFS_NS, REFS_NAME); // NOI18N
132
            references = aux.getConfigurationFragment(REFS_NAME, REFS_NS, true);
126
        }
133
        }
127
        return references;
134
        return references;
128
    }
135
    }
Lines 132-180 Link Here
132
     */
139
     */
133
    private void storeReferences(Element references) {
140
    private void storeReferences(Element references) {
134
        assert ProjectManager.mutex().isWriteAccess();
141
        assert ProjectManager.mutex().isWriteAccess();
135
        assert references != null && references.getLocalName().equals(REFS_NAME) && REFS_NS.equals(references.getNamespaceURI());
142
        assert references != null && references.getLocalName().equals(REFS_NAME) && 
143
            (REFS_NS.equals(references.getNamespaceURI()) || REFS_NS2.equals(references.getNamespaceURI()));
136
        aux.putConfigurationFragment(references, true);
144
        aux.putConfigurationFragment(references, true);
137
    }
145
    }
138
    
146
    
147
    private void removeOldReferences() {
148
        assert ProjectManager.mutex().isWriteAccess();
149
        aux.removeConfigurationFragment(REFS_NS, REFS_NAME, true);
150
    }
151
    
139
    /**
152
    /**
140
     * Add a reference to an artifact coming from a foreign project.
153
     * Add a reference to an artifact coming from a foreign project.
141
     * <p>
154
     * <p>
142
     * Records the name of the foreign project.
155
     * For more info see {@link #addReference(AntArtifact, URI)}.
143
     * Normally the foreign project name is that project's code name,
144
     * but it may be uniquified if that name is already taken to refer
145
     * to a different project with the same code name.
146
     * <p>
147
     * Adds a project property if necessary to refer to its location of the foreign
148
     * project - a shared property if the foreign project
149
     * is {@link CollocationQuery collocated} with this one, else a private property.
150
     * This property is named <samp>project.<i>foreignProjectName</i></samp>.
151
     * Example: <samp>project.mylib=../mylib</samp>
152
     * <p>
153
     * Adds a project property to refer to the location of the artifact itself.
154
     * This property is named <samp>reference.<i>foreignProjectName</i>.<i>targetName</i></samp>
155
     * and will use <samp>${project.<i>foreignProjectName</i>}</samp> and be a shared
156
     * property - unless the artifact location is an absolute URI, in which case the property
157
     * will also be private.
158
     * Example: <samp>reference.mylib.jar=${project.mylib}/dist/mylib.jar</samp>
159
     * <p>
160
     * Also records the artifact type, (relative) script path, and build and
161
     * clean target names.
162
     * <p>
163
     * If the reference already exists (keyed by foreign project object
164
     * and target name), nothing is done, unless some other field (script location,
165
     * clean target name, or artifact type) needed to be updated, in which case
166
     * the new information replaces the old. Similarly, the artifact location
167
     * property is updated if necessary.
168
     * <p>
169
     * Acquires write access.
170
     * @param artifact the artifact to add
156
     * @param artifact the artifact to add
171
     * @return true if a reference or some property was actually added or modified,
157
     * @return true if a reference or some property was actually added or modified,
172
     *         false if everything already existed and was not modified
158
     *         false if everything already existed and was not modified
173
     * @throws IllegalArgumentException if the artifact is not associated with a project
159
     * @throws IllegalArgumentException if the artifact is not associated with a project
160
     * @deprecated to add reference use {@link #addReference(AntArtifact, URI)};
161
     *   to check whether reference exist or not use {@link #isReferenced(AntArtifact, URI)}.
162
     *   This method creates reference for the first artifact location only.
174
     */
163
     */
175
    public boolean addReference(final AntArtifact artifact) throws IllegalArgumentException {
164
    public boolean addReference(final AntArtifact artifact) throws IllegalArgumentException {
176
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
165
        Object ret[] = addReference0(artifact, artifact.getArtifactLocations()[0]);
166
        return ((Boolean)ret[0]).booleanValue();
167
    }
168
169
    // @return array of two elements: [Boolean - any modification, String - reference]
170
    private Object[] addReference0(final AntArtifact artifact, final URI location) throws IllegalArgumentException {
171
        return ((List)ProjectManager.mutex().writeAccess(new Mutex.Action() {
177
            public Object run() {
172
            public Object run() {
173
                int index = findLocationIndex(artifact, location);
178
                Project forProj = artifact.getProject();
174
                Project forProj = artifact.getProject();
179
                if (forProj == null) {
175
                if (forProj == null) {
180
                    throw new IllegalArgumentException("No project associated with " + artifact); // NOI18N
176
                    throw new IllegalArgumentException("No project associated with " + artifact); // NOI18N
Lines 187-236 Link Here
187
                if (forProjName == null) {
183
                if (forProjName == null) {
188
                    forProjName = generateUniqueID(projName, "project.", forProjDir.getAbsolutePath());
184
                    forProjName = generateUniqueID(projName, "project.", forProjDir.getAbsolutePath());
189
                }
185
                }
186
                RawReference ref;
190
                File scriptFile = artifact.getScriptLocation();
187
                File scriptFile = artifact.getScriptLocation();
191
                URI scriptLocation;
188
                if (canUseVersion10(artifact, forProjDir)) {
192
                String rel = PropertyUtils.relativizeFile(forProjDir, scriptFile);
189
                String rel = PropertyUtils.relativizeFile(forProjDir, scriptFile);
190
                    URI scriptLocation;
193
                try {
191
                try {
194
                    scriptLocation = new URI(null, null, rel, null);
192
                    scriptLocation = new URI(null, null, rel, null);
195
                } catch (URISyntaxException ex) {
193
                } catch (URISyntaxException ex) {
196
                    scriptLocation = forProjDir.toURI().relativize(scriptFile.toURI());
194
                    scriptLocation = forProjDir.toURI().relativize(scriptFile.toURI());
197
                }
195
                }
198
                RawReference ref = new RawReference(forProjName, artifact.getType(), scriptLocation, artifact.getTargetName(), artifact.getCleanTargetName(), artifact.getID());
196
                    ref = new RawReference(forProjName, artifact.getType(), scriptLocation, artifact.getTargetName(), artifact.getCleanTargetName(), artifact.getID());
199
                Element references = loadReferences(true);
197
                } else {
200
                boolean success;
198
                    String scriptLocation;
201
                try {
199
                    if (scriptFile.getAbsolutePath().startsWith(forProjDir.getAbsolutePath())) {
202
                    success = addRawReference(ref, references);
200
                        String rel = PropertyUtils.relativizeFile(forProjDir, scriptFile);
203
                } catch (IllegalArgumentException e) {
201
                        assert rel != null : "Relativization must succeed for files: "+forProjDir+ " "+scriptFile;
204
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
202
                        scriptLocation = "${project."+forProjName+"}/"+rel;
205
                    return Boolean.FALSE;
203
                    } else {
206
                }
204
                        scriptLocation = "build.script.reference." + forProjName;
207
                if (success) {
205
                        setPathProperty(forProjDir, scriptFile, scriptLocation);
208
                    storeReferences(references);
206
                        scriptLocation = "${"+scriptLocation+"}";
207
                    }
208
                    ref = new RawReference(forProjName, artifact.getType(), scriptLocation, 
209
                        artifact.getTargetName(), artifact.getCleanTargetName(), 
210
                        artifact.getID(), artifact.getProperties());
209
                }
211
                }
212
                boolean success = addRawReference0(ref);
210
                // Set up ${project.whatever}.
213
                // Set up ${project.whatever}.
211
                FileObject myProjDirFO = AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory();
214
                FileObject myProjDirFO = AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory();
212
                File myProjDir = FileUtil.toFile(myProjDirFO);
215
                File myProjDir = FileUtil.toFile(myProjDirFO);
213
                String forProjPath;
216
                if (setPathProperty(myProjDir, forProjDir, "project." + forProjName)) {
214
                String propertiesFile;
215
                if (CollocationQuery.areCollocated(myProjDir, forProjDir)) {
216
                    // Fine, using a relative path to subproject.
217
                    forProjPath = PropertyUtils.relativizeFile(myProjDir, forProjDir);
218
                    assert forProjPath != null : "These dirs are not really collocated: " + myProjDir + " & " + forProjDir;
219
                    propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH;
220
                } else {
221
                    // Use an absolute path.
222
                    forProjPath = forProjDir.getAbsolutePath();
223
                    propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH;
224
                }
225
                EditableProperties props = h.getProperties(propertiesFile);
226
                String forProjPathProp = "project." + forProjName; // NOI18N
227
                if (!forProjPath.equals(props.getProperty(forProjPathProp))) {
228
                    props.put(forProjPathProp, forProjPath);
229
                    h.putProperties(propertiesFile, props);
230
                    success = true;
217
                    success = true;
231
                }
218
                }
232
                // Set up ${reference.whatever.whatever}.
219
                // Set up ${reference.whatever.whatever}.
233
                URI artFile = artifact.getArtifactLocation();
220
                String propertiesFile;
221
                String forProjPathProp = "project." + forProjName; // NOI18N
222
                URI artFile = location;
234
                String refPath;
223
                String refPath;
235
                if (artFile.isAbsolute()) {
224
                if (artFile.isAbsolute()) {
236
                    refPath = new File(artFile).getAbsolutePath();
225
                    refPath = new File(artFile).getAbsolutePath();
Lines 239-252 Link Here
239
                    refPath = "${" + forProjPathProp + "}/" + artFile.getPath(); // NOI18N
228
                    refPath = "${" + forProjPathProp + "}/" + artFile.getPath(); // NOI18N
240
                    propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH;
229
                    propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH;
241
                }
230
                }
242
                props = h.getProperties(propertiesFile);
231
                EditableProperties props = h.getProperties(propertiesFile);
243
                String refPathProp = "reference." + forProjName + '.' + getUsableReferenceID(artifact.getID()); // NOI18N
232
                String refPathProp = "reference." + forProjName + '.' + getUsableReferenceID(artifact.getID()); // NOI18N
233
                if (index > 0) {
234
                    refPathProp += "."+index;
235
                }
244
                if (!refPath.equals(props.getProperty(refPathProp))) {
236
                if (!refPath.equals(props.getProperty(refPathProp))) {
245
                    props.put(refPathProp, refPath);
237
                    props.put(refPathProp, refPath);
246
                    h.putProperties(propertiesFile, props);
238
                    h.putProperties(propertiesFile, props);
247
                    success = true;
239
                    success = true;
248
                }
240
                }
249
                return Boolean.valueOf(success);
241
                ArrayList l = new ArrayList();
242
                l.add(Boolean.valueOf(success));
243
                l.add("${"+refPathProp+"}"); // NOI18N
244
                return l;
245
            }
246
        })).toArray(new Object[2]);
247
    }
248
    
249
    private int findLocationIndex(final AntArtifact artifact, final URI location) throws IllegalArgumentException {
250
        if (location == null) {
251
            throw new IllegalArgumentException("location cannot be null");
252
        }
253
        URI uris[] = artifact.getArtifactLocations();
254
        for (int i=0; i<uris.length; i++) {
255
            if (uris[i].equals(location)) {
256
                return i;
257
            }
258
        }
259
        throw new IllegalArgumentException("location ("+location+") must be in AntArtifact's locations ("+artifact+")");
260
    }
261
262
    /**
263
     * Test whether the artifact can be stored as /1 artifact or not.
264
     */
265
    private static boolean canUseVersion10(AntArtifact aa, File projectDirectory) {
266
        // is there multiple outputs?
267
        if (aa.getArtifactLocations().length > 1) {
268
            return false;
269
        }
270
        // has some properties?
271
        if (aa.getProperties().keySet().size() > 0) {
272
            return false;
273
        }
274
        // does Ant script lies under project directory?
275
        if (!aa.getScriptLocation().getAbsolutePath().startsWith(projectDirectory.getAbsolutePath())) {
276
            return false;
277
        }
278
        return true;
279
    }
280
281
    /**
282
     * Helper method which checks collocation status of two files and based on
283
     * that it will in private or project properties file set up property with
284
     * the given name and with absolute or relative path value.
285
     * @return was there any change or not
286
     */
287
    private boolean setPathProperty(File base, File path, String propertyName) {
288
        String value;
289
        String propertiesFile;
290
        if (CollocationQuery.areCollocated(base, path)) {
291
            // Fine, using a relative path to subproject.
292
            value = PropertyUtils.relativizeFile(base, path);
293
            assert value != null : "These dirs are not really collocated: " + base + " & " + path;
294
            propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH;
295
        } else {
296
            // Use an absolute path.
297
            value = path.getAbsolutePath();
298
            propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH;
299
        }
300
        EditableProperties props = h.getProperties(propertiesFile);
301
        if (!value.equals(props.getProperty(propertyName))) {
302
            props.put(propertyName, value);
303
            h.putProperties(propertiesFile, props);
304
            // check presence of this property in opposite property file and
305
            // remove it if necessary
306
            propertiesFile = (propertiesFile == AntProjectHelper.PROJECT_PROPERTIES_PATH ? 
307
                AntProjectHelper.PRIVATE_PROPERTIES_PATH : AntProjectHelper.PROJECT_PROPERTIES_PATH);
308
            props = h.getProperties(propertiesFile);
309
            if (props.remove(propertyName) != null) {
310
                h.putProperties(propertiesFile, props);
311
            }
312
            return true;
313
        } else {
314
            return false;
315
        }
316
        
317
    }
318
    
319
    /**
320
     * Add a reference to an artifact's location coming from a foreign project.
321
     * <p>
322
     * Records the name of the foreign project.
323
     * Normally the foreign project name is that project's code name,
324
     * but it may be uniquified if that name is already taken to refer
325
     * to a different project with the same code name.
326
     * <p>
327
     * Adds a project property if necessary to refer to its location of the foreign
328
     * project - a shared property if the foreign project
329
     * is {@link CollocationQuery collocated} with this one, else a private property.
330
     * This property is named <samp>project.<i>foreignProjectName</i></samp>.
331
     * Example: <samp>project.mylib=../mylib</samp>
332
     * <p>
333
     * Adds a project property to refer to the artifact's location.
334
     * This property is named <samp>reference.<i>foreignProjectName</i>.<i>targetName</i></samp>
335
     * and will use <samp>${project.<i>foreignProjectName</i>}</samp> and be a shared
336
     * property - unless the artifact location is an absolute URI, in which case the property
337
     * will also be private.
338
     * Example: <samp>reference.mylib.jar=${project.mylib}/dist/mylib.jar</samp>
339
     * <p>
340
     * Also records the artifact type, (relative) script path, and build and
341
     * clean target names.
342
     * <p>
343
     * If the reference already exists (keyed by foreign project object
344
     * and target name), nothing is done, unless some other field (script location,
345
     * clean target name, or artifact type) needed to be updated, in which case
346
     * the new information replaces the old. Similarly, the artifact location
347
     * property is updated if necessary.
348
     * <p>
349
     * Acquires write access.
350
     * @param artifact the artifact to add
351
     * @param location the artifact's location to create reference to
352
     * @return name of reference which was created or already existed
353
     * @throws IllegalArgumentException if the artifact is not associated with a project
354
     *   or if the location is not artifact's location
355
     * @since X.XX
356
     */
357
    public String addReference(final AntArtifact artifact, URI location) throws IllegalArgumentException {
358
        Object ret[] = addReference0(artifact, location);
359
        return (String)ret[1];
360
    }
361
    
362
    /**
363
     * Tests whether reference for artifact's location was already created by
364
     * {@link #addReference(AntArtifact, URI)} for this project or not. This
365
     * method returns false also in case when reference exist but needs to be
366
     * updated.
367
     * <p>
368
     * Acquires read access.
369
     * @param artifact the artifact to add
370
     * @param location the artifact's location to create reference to
371
     * @return true if already referenced
372
     * @throws IllegalArgumentException if the artifact is not associated with a project
373
     *   or if the location is not artifact's location
374
     * @since X.XX
375
     */
376
    public boolean isReferenced(final AntArtifact artifact, final URI location) throws IllegalArgumentException {
377
        return ((Boolean)ProjectManager.mutex().readAccess(new Mutex.Action() {
378
            public Object run() {
379
                int index = findLocationIndex(artifact, location);
380
                Project forProj = artifact.getProject();
381
                if (forProj == null) {
382
                    throw new IllegalArgumentException("No project associated with " + artifact); // NOI18N
383
                }
384
                File forProjDir = FileUtil.toFile(forProj.getProjectDirectory());
385
                assert forProjDir != null : forProj.getProjectDirectory();
386
                String projName = getUsableReferenceID(ProjectUtils.getInformation(forProj).getName());
387
                String forProjName = findReferenceID(projName, "project.", forProjDir.getAbsolutePath());
388
                if (forProjName == null) {
389
                    return Boolean.FALSE;
390
                }
391
                RawReference ref = getRawReference(forProjName, getUsableReferenceID(artifact.getID()));
392
                if (ref == null) {
393
                    return Boolean.FALSE;
394
                }
395
                File script = h.resolveFile(eval.evaluate(ref.getScriptLocationValue()));
396
                if (!artifact.getType().equals(ref.getArtifactType()) ||
397
                        !artifact.getID().equals(ref.getID()) ||
398
                        !artifact.getScriptLocation().equals(script) ||
399
                        !artifact.getProperties().equals(ref.getProperties()) ||
400
                        !artifact.getTargetName().equals(ref.getTargetName()) ||
401
                        !artifact.getCleanTargetName().equals(ref.getCleanTargetName())) {
402
                    return Boolean.FALSE;
403
                }
404
                
405
                String reference = "reference." + forProjName + '.' + getUsableReferenceID(artifact.getID()); // NOI18N
406
                if (index > 0) {
407
                    reference += "."+index;
408
                }
409
                return Boolean.valueOf(eval.getProperty(reference) != null);
250
            }
410
            }
251
        })).booleanValue();
411
        })).booleanValue();
252
    }
412
    }
Lines 273-297 Link Here
273
    public boolean addRawReference(final RawReference ref) {
433
    public boolean addRawReference(final RawReference ref) {
274
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
434
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
275
            public Object run() {
435
            public Object run() {
276
                Element references = loadReferences(true);
277
                boolean success;
278
                try {
436
                try {
279
                    success = addRawReference(ref, references);
437
                    return Boolean.valueOf(addRawReference0(ref));
280
                } catch (IllegalArgumentException e) {
438
                } catch (IllegalArgumentException e) {
281
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
439
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
282
                    return Boolean.FALSE;
440
                    return Boolean.FALSE;
283
                }
441
                }
284
                if (success) {
442
            }
443
        })).booleanValue();
444
    }
445
    
446
    private boolean addRawReference0(final RawReference ref) throws IllegalArgumentException {
447
        Element references = loadReferences();
448
        if (references == null) {
449
            references = XMLUtil.createDocument("ignore", null, null, null).createElementNS(ref.getNS(), REFS_NAME); // NOI18N
450
        }
451
        boolean modified = false;
452
        if (references.getNamespaceURI().equals(REFS_NS) && ref.getNS().equals(REFS_NS2)) {
453
            // upgrade all references to version /2 here:
454
            references = upgradeTo20(references);
455
            removeOldReferences();
456
            modified = true;
457
        }
458
        modified = updateRawReferenceElement(ref, references);
459
        if (modified) {
285
                    storeReferences(references);
460
                    storeReferences(references);
286
                    return Boolean.TRUE;
287
                } else {
288
                    return Boolean.FALSE;
289
                }
461
                }
462
        return modified;
290
            }
463
            }
291
        })).booleanValue();
464
    
465
    private Element upgradeTo20(Element references) {
466
        Element references20 = XMLUtil.createDocument("ignore", null, null, null).createElementNS(REFS_NS2, REFS_NAME); // NOI18N
467
        RawReference rr[] = getRawReferences(references);
468
        for (int i=0; i<rr.length; i++) {
469
            rr[i].upgrade();
470
            updateRawReferenceElement(rr[i], references20);
471
        }
472
        return references20;
292
    }
473
    }
293
    
474
    
294
    private static boolean addRawReference(RawReference ref, Element references) throws IllegalArgumentException {
475
    private static boolean updateRawReferenceElement(RawReference ref, Element references) throws IllegalArgumentException {
295
        // Linear search; always keeping references sorted first by foreign project
476
        // Linear search; always keeping references sorted first by foreign project
296
        // name, then by target name.
477
        // name, then by target name.
297
        Element nextRefEl = null;
478
        Element nextRefEl = null;
Lines 314-320 Link Here
314
                if (testRef.getID().equals(ref.getID())) {
495
                if (testRef.getID().equals(ref.getID())) {
315
                    // Key match, check if it needs to be updated.
496
                    // Key match, check if it needs to be updated.
316
                    if (testRef.getArtifactType().equals(ref.getArtifactType()) &&
497
                    if (testRef.getArtifactType().equals(ref.getArtifactType()) &&
317
                            testRef.getScriptLocation().equals(ref.getScriptLocation()) &&
498
                            testRef.getScriptLocationValue().equals(ref.getScriptLocationValue()) &&
499
                            testRef.getProperties().equals(ref.getProperties()) &&
318
                            testRef.getTargetName().equals(ref.getTargetName()) &&
500
                            testRef.getTargetName().equals(ref.getTargetName()) &&
319
                            testRef.getCleanTargetName().equals(ref.getCleanTargetName())) {
501
                            testRef.getCleanTargetName().equals(ref.getCleanTargetName())) {
320
                        // Match on other fields. Return without changing anything.
502
                        // Match on other fields. Return without changing anything.
Lines 333-339 Link Here
333
            }
515
            }
334
        }
516
        }
335
        // Need to insert a new record before nextRef.
517
        // Need to insert a new record before nextRef.
336
        Element newRefEl = ref.toXml(references.getOwnerDocument());
518
        Element newRefEl = ref.toXml(references.getNamespaceURI(), references.getOwnerDocument());
337
        // Note: OK if nextRefEl == null, that means insert as last child.
519
        // Note: OK if nextRefEl == null, that means insert as last child.
338
        references.insertBefore(newRefEl, nextRefEl);
520
        references.insertBefore(newRefEl, nextRefEl);
339
        return true;
521
        return true;
Lines 355-379 Link Here
355
     * @param id the ID of the build artifact (usually build target name)
537
     * @param id the ID of the build artifact (usually build target name)
356
     * @return true if a reference or some property was actually removed,
538
     * @return true if a reference or some property was actually removed,
357
     *         false if the reference was not there and no property was removed
539
     *         false if the reference was not there and no property was removed
540
     * @deprecated use {@link #destroyReference} instead; was unused anyway
358
     */
541
     */
359
    public boolean removeReference(final String foreignProjectName, final String id) {
542
    public boolean removeReference(final String foreignProjectName, final String id) {
360
        return removeReference(foreignProjectName, id, false);
543
        return removeReference(foreignProjectName, id, false, null);
361
    }
544
    }
362
    
545
    
363
    private boolean removeReference(final String foreignProjectName, final String id, final boolean escaped) {
546
    private boolean removeReference(final String foreignProjectName, final String id, final boolean escaped, final String reference) {
364
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
547
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
365
            public Object run() {
548
            public Object run() {
366
                Element references = loadReferences(true);
367
                boolean success;
549
                boolean success;
368
                try {
550
                try {
369
                    success = removeRawReference(foreignProjectName, id, references, escaped);
551
                    success = removeRawReference0(foreignProjectName, id, escaped);
370
                } catch (IllegalArgumentException e) {
552
                } catch (IllegalArgumentException e) {
371
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
553
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
372
                    return Boolean.FALSE;
554
                    return Boolean.FALSE;
373
                }
555
                }
374
                if (success) {
375
                    storeReferences(references);
376
                }
377
                // Note: try to delete obsoleted properties from both project.properties
556
                // Note: try to delete obsoleted properties from both project.properties
378
                // and private.properties, just in case.
557
                // and private.properties, just in case.
379
                String[] PROPS_PATHS = {
558
                String[] PROPS_PATHS = {
Lines 382-388 Link Here
382
                };
561
                };
383
                // Check whether there are any other references using foreignProjectName.
562
                // Check whether there are any other references using foreignProjectName.
384
                // If not, we can delete ${project.foreignProjectName}.
563
                // If not, we can delete ${project.foreignProjectName}.
385
                RawReference[] refs = getRawReferences(references);
564
                RawReference[] refs = new RawReference[0];
565
                Element references = loadReferences();
566
                if (references != null) {
567
                    refs = getRawReferences(references);
568
                }
386
                boolean deleteProjProp = true;
569
                boolean deleteProjProp = true;
387
                for (int i = 0; i < refs.length; i++) {
570
                for (int i = 0; i < refs.length; i++) {
388
                    if (refs[i].getForeignProjectName().equals(foreignProjectName)) {
571
                    if (refs[i].getForeignProjectName().equals(foreignProjectName)) {
Lines 401-407 Link Here
401
                        }
584
                        }
402
                    }
585
                    }
403
                }
586
                }
404
                String refProp = "reference." + foreignProjectName + '.' + getUsableReferenceID(id); // NOI18N
587
                
588
                String refProp = reference;
589
                if (refProp == null) {
590
                    refProp = "reference." + foreignProjectName + '.' + getUsableReferenceID(id); // NOI18N
591
                }
592
                // remove also build script property if exist any:
593
                String buildScriptProperty = "build.script.reference." + foreignProjectName;
405
                for (int i = 0; i < PROPS_PATHS.length; i++) {
594
                for (int i = 0; i < PROPS_PATHS.length; i++) {
406
                    EditableProperties props = h.getProperties(PROPS_PATHS[i]);
595
                    EditableProperties props = h.getProperties(PROPS_PATHS[i]);
407
                    if (props.containsKey(refProp)) {
596
                    if (props.containsKey(refProp)) {
Lines 409-414 Link Here
409
                        h.putProperties(PROPS_PATHS[i], props);
598
                        h.putProperties(PROPS_PATHS[i], props);
410
                        success = true;
599
                        success = true;
411
                    }
600
                    }
601
                    if (props.containsKey(buildScriptProperty)) {
602
                        props.remove(buildScriptProperty);
603
                        h.putProperties(PROPS_PATHS[i], props);
604
                        success = true;
605
                    }
412
                }
606
                }
413
                return Boolean.valueOf(success);
607
                return Boolean.valueOf(success);
414
            }
608
            }
Lines 424-431 Link Here
424
     * @param fileReference file reference as created by 
618
     * @param fileReference file reference as created by 
425
     *    {@link #createForeignFileReference(File, String)}
619
     *    {@link #createForeignFileReference(File, String)}
426
     * @return true if the reference was actually removed; otherwise false
620
     * @return true if the reference was actually removed; otherwise false
621
     * @deprecated use {@link #destroyReference} instead; was unused anyway
427
     */
622
     */
428
    public boolean removeReference(final String fileReference) {
623
    public boolean removeReference(final String fileReference) {
624
        return removeFileReference(fileReference);
625
    }
626
    
627
    private boolean removeFileReference(final String fileReference) {
429
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
628
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
430
            public Object run() {
629
            public Object run() {
431
                boolean success = false;
630
                boolean success = false;
Lines 468-492 Link Here
468
    public boolean removeRawReference(final String foreignProjectName, final String id) {
667
    public boolean removeRawReference(final String foreignProjectName, final String id) {
469
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
668
        return ((Boolean)ProjectManager.mutex().writeAccess(new Mutex.Action() {
470
            public Object run() {
669
            public Object run() {
471
                Element references = loadReferences(true);
472
                boolean success;
473
                try {
670
                try {
474
                    success = removeRawReference(foreignProjectName, id, references, false);
671
                    return Boolean.valueOf(removeRawReference0(foreignProjectName, id, false));
475
                } catch (IllegalArgumentException e) {
672
                } catch (IllegalArgumentException e) {
476
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
673
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
477
                    return Boolean.FALSE;
674
                    return Boolean.FALSE;
478
                }
675
                }
676
            }
677
        })).booleanValue();
678
    }
679
    
680
    private boolean removeRawReference0(final String foreignProjectName, final String id, boolean escaped) throws IllegalArgumentException {
681
        Element references = loadReferences();
682
        if (references == null) {
683
            return false;
684
        }
685
        boolean success = removeRawReferenceElement(foreignProjectName, id, references, escaped);
479
                if (success) {
686
                if (success) {
480
                    storeReferences(references);
687
                    storeReferences(references);
481
                    return Boolean.TRUE;
482
                } else {
483
                    return Boolean.FALSE;
484
                }
688
                }
485
            }
689
        return success;
486
        })).booleanValue();
487
    }
690
    }
488
    
691
    
489
    private static boolean removeRawReference(String foreignProjectName, String id, Element references, boolean escaped) throws IllegalArgumentException {
692
    private static boolean removeRawReferenceElement(String foreignProjectName, String id, Element references, boolean escaped) throws IllegalArgumentException {
490
        // As with addRawReference, do a linear search through.
693
        // As with addRawReference, do a linear search through.
491
        List/*<Element>*/subEls = Util.findSubElements(references);
694
        List/*<Element>*/subEls = Util.findSubElements(references);
492
        Iterator it = subEls.iterator();
695
        Iterator it = subEls.iterator();
Lines 530-536 Link Here
530
    public RawReference[] getRawReferences() {
733
    public RawReference[] getRawReferences() {
531
        return (RawReference[])ProjectManager.mutex().readAccess(new Mutex.Action() {
734
        return (RawReference[])ProjectManager.mutex().readAccess(new Mutex.Action() {
532
            public Object run() {
735
            public Object run() {
533
                Element references = loadReferences(false);
736
                Element references = loadReferences();
534
                if (references != null) {
737
                if (references != null) {
535
                    try {
738
                    try {
536
                        return getRawReferences(references);
739
                        return getRawReferences(references);
Lines 573-579 Link Here
573
    RawReference getRawReference(final String foreignProjectName, final String id, final boolean escaped) {
776
    RawReference getRawReference(final String foreignProjectName, final String id, final boolean escaped) {
574
        return (RawReference)ProjectManager.mutex().readAccess(new Mutex.Action() {
777
        return (RawReference)ProjectManager.mutex().readAccess(new Mutex.Action() {
575
            public Object run() {
778
            public Object run() {
576
                Element references = loadReferences(false);
779
                Element references = loadReferences();
577
                if (references != null) {
780
                if (references != null) {
578
                    try {
781
                    try {
579
                        return getRawReference(foreignProjectName, id, references, escaped);
782
                        return getRawReference(foreignProjectName, id, references, escaped);
Lines 638-652 Link Here
638
                    String propertiesFile;
841
                    String propertiesFile;
639
                    String path;
842
                    String path;
640
                    File myProjDir = FileUtil.toFile(AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory());
843
                    File myProjDir = FileUtil.toFile(AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory());
641
                    if (CollocationQuery.areCollocated(myProjDir, file)) {
642
                        propertiesFile = AntProjectHelper.PROJECT_PROPERTIES_PATH;
643
                        path = PropertyUtils.relativizeFile(myProjDir, file);
644
                        assert path != null : "expected relative path from " + myProjDir + " to " + file;
645
                    } else {
646
                        propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH;
647
                        path = file.getAbsolutePath();
648
                    }
649
                    EditableProperties props = h.getProperties(propertiesFile);
650
                    String fileID = file.getName();
844
                    String fileID = file.getName();
651
                    // if the file is folder then add to ID string also parent folder name,
845
                    // if the file is folder then add to ID string also parent folder name,
652
                    // i.e. if external source folder name is "src" the ID will
846
                    // i.e. if external source folder name is "src" the ID will
Lines 660-669 Link Here
660
                    if (prop == null) {
854
                    if (prop == null) {
661
                        prop = generateUniqueID(fileID, "file.reference.", file.getAbsolutePath()); // NOI18N
855
                        prop = generateUniqueID(fileID, "file.reference.", file.getAbsolutePath()); // NOI18N
662
                    }
856
                    }
663
                    if (!path.equals(props.getProperty("file.reference." + prop))) { // NOI18N
857
                    setPathProperty(myProjDir, file, "file.reference." + prop);
664
                        props.put("file.reference." + prop, path); // NOI18N
665
                        h.putProperties(propertiesFile, props);
666
                    }
667
                    return "${file.reference." + prop + '}'; // NOI18N
858
                    return "${file.reference." + prop + '}'; // NOI18N
668
                }
859
                }
669
            }
860
            }
Lines 742-752 Link Here
742
     * @param artifact a known build artifact to refer to
933
     * @param artifact a known build artifact to refer to
743
     * @return a string which can refer to that artifact file somehow
934
     * @return a string which can refer to that artifact file somehow
744
     * @throws IllegalArgumentException if the artifact is not associated with a project
935
     * @throws IllegalArgumentException if the artifact is not associated with a project
936
     * @deprecated use {@link #addReference(AntArtifact, URI)} instead
745
     */
937
     */
746
    public String createForeignFileReference(AntArtifact artifact) throws IllegalArgumentException {
938
    public String createForeignFileReference(AntArtifact artifact) throws IllegalArgumentException {
747
        addReference(artifact);
939
        Object ret[] = addReference0(artifact, artifact.getArtifactLocations()[0]);
748
        String projID = findReferenceID(artifact);
940
        return (String)ret[1];
749
        return "${reference." + projID + '.' + getUsableReferenceID(artifact.getID()) + '}'; // NOI18N
750
    }
941
    }
751
942
752
    /**
943
    /**
Lines 758-764 Link Here
758
    }
949
    }
759
    
950
    
760
    
951
    
761
    private static final Pattern FOREIGN_FILE_REFERENCE = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\}"); // NOI18N
952
    private static final Pattern FOREIGN_FILE_REFERENCE = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\.([\\d&&[^.${}]]+)\\}"); // NOI18N
953
    private static final Pattern FOREIGN_FILE_REFERENCE_OLD = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\}"); // NOI18N
762
    private static final Pattern FOREIGN_PLAIN_FILE_REFERENCE = Pattern.compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N
954
    private static final Pattern FOREIGN_PLAIN_FILE_REFERENCE = Pattern.compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N
763
    
955
    
764
    /**
956
    /**
Lines 769-797 Link Here
769
     * <p>Acquires read access.
961
     * <p>Acquires read access.
770
     * @param reference a reference string as present in an Ant property
962
     * @param reference a reference string as present in an Ant property
771
     * @return a corresponding Ant artifact object if there is one, else null
963
     * @return a corresponding Ant artifact object if there is one, else null
964
     * @deprecated use {@link #findArtifactAndLocation} instead
772
     */
965
     */
773
    public AntArtifact getForeignFileReferenceAsArtifact(final String reference) {
966
    public AntArtifact getForeignFileReferenceAsArtifact(final String reference) {
774
        return (AntArtifact)ProjectManager.mutex().readAccess(new Mutex.Action() {
967
        Object ret[] = findArtifactAndLocation(reference);
968
        return (AntArtifact)ret[0];
969
    }
970
    
971
    /**
972
     * Try to find an <code>AntArtifact</code> object and location corresponding
973
     * to a given reference. If the supplied string is not a recognized 
974
     * reference to a build artifact, returns null.
975
     * <p>
976
     * Acquires read access.
977
     * @param reference a reference string as present in an Ant property and as
978
     *   created by {@link #addReference(AntArtifact, URI)}
979
     * @return always returns array of two items. The items may be null. First
980
     *   one is instance of AntArtifact and second is instance of URI and is 
981
     *   AntArtifact's location
982
     * @since X.XX
983
     */
984
    public Object[] findArtifactAndLocation(final String reference) {
985
        return ((List)ProjectManager.mutex().readAccess(new Mutex.Action() {
775
            public Object run() {
986
            public Object run() {
987
                ArrayList l = new ArrayList();
988
                AntArtifact aa = null;
776
                Matcher m = FOREIGN_FILE_REFERENCE.matcher(reference);
989
                Matcher m = FOREIGN_FILE_REFERENCE.matcher(reference);
777
                if (m.matches()) {
990
                boolean matches = m.matches();
991
                int index = 0;
992
                if (!matches) {
993
                    m = FOREIGN_FILE_REFERENCE_OLD.matcher(reference);
994
                    matches = m.matches();
995
                } else {
996
                    try {
997
                        index = Integer.parseInt(m.group(3));
998
                    } catch (NumberFormatException ex) {
999
                        ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, 
1000
                            "Could not parse reference ("+reference+") for the jar index. " + // NOI18N
1001
                            "Expected number: "+m.group(3)); // NOI18N
1002
                        matches = false;
1003
                    }
1004
                }
1005
                if (matches) {
778
                    RawReference ref = getRawReference(m.group(1), m.group(2), true);
1006
                    RawReference ref = getRawReference(m.group(1), m.group(2), true);
779
                    if (ref != null) {
1007
                    if (ref != null) {
780
                        return ref.toAntArtifact(ReferenceHelper.this);
1008
                        aa = ref.toAntArtifact(ReferenceHelper.this);
781
                    }
1009
                    }
782
                }
1010
                }
783
                return null;
1011
                if (aa == null) {
1012
                    l.add(null);
1013
                    l.add(null);
1014
                    return l;
784
            }
1015
            }
785
        });
1016
                assert index <= aa.getArtifactLocations().length;
1017
                URI uri = aa.getArtifactLocations()[index];
1018
                l.add(aa);
1019
                l.add(uri);
1020
                return l;
1021
            }
1022
        })).toArray(new Object[2]);
1023
    }
1024
    
1025
    /**
1026
     * Remove a reference to a foreign file from the project.
1027
     * See {@link #destroyReference} for more information.
1028
     * @param reference an Ant-interpretable foreign file reference as created e.g.
1029
     *                  by {@link #createForeignFileReference(File,String)} or
1030
     *                  by {@link #createForeignFileReference(AntArtifact)}
1031
     * @deprecated use {@link #destroyReference} instead which does exactly 
1032
     *   the same but has more appropriate name
1033
     */
1034
    public void destroyForeignFileReference(String reference) {
1035
        destroyReference(reference);
786
    }
1036
    }
787
    
1037
    
788
    /**
1038
    /**
789
     * Remove a reference to a foreign file from the project.
1039
     * Remove a reference to a foreign file from the project.
790
     * If the passed string consists of an Ant property reference corresponding to
1040
     * If the passed string consists of an Ant property reference corresponding to
791
     * a known inter-project reference created by 
1041
     * a known inter-project reference created by 
792
     * {@link #createForeignFileReference(AntArtifact)} or file reference created by
1042
     * {@link #addReference(AntArtifact, URI)} or file reference created by
793
     * {@link #createForeignFileReference(File, String)}, that reference is removed using
1043
     * {@link #createForeignFileReference(File, String)}, that reference is removed.
794
     * {@link #removeReference(String, String)} or {@link #removeReference(String)}.
795
     * Since this would break any other identical foreign
1044
     * Since this would break any other identical foreign
796
     * file references present in the project, you should first confirm that this
1045
     * file references present in the project, you should first confirm that this
797
     * reference was the last one of its kind (by string match).
1046
     * reference was the last one of its kind (by string match).
Lines 803-822 Link Here
803
     * @param reference an Ant-interpretable foreign file reference as created e.g.
1052
     * @param reference an Ant-interpretable foreign file reference as created e.g.
804
     *                  by {@link #createForeignFileReference(File,String)} or
1053
     *                  by {@link #createForeignFileReference(File,String)} or
805
     *                  by {@link #createForeignFileReference(AntArtifact)}
1054
     *                  by {@link #createForeignFileReference(AntArtifact)}
1055
     * @return true if reference was really destroyed or not
1056
     * @since X.XX
806
     */
1057
     */
807
    public void destroyForeignFileReference(String reference) {
1058
    public boolean destroyReference(String reference) {
808
        Matcher m = FOREIGN_FILE_REFERENCE.matcher(reference);
1059
        Matcher m = FOREIGN_FILE_REFERENCE.matcher(reference);
809
        if (m.matches()) {
1060
        boolean matches = m.matches();
1061
        if (!matches) {
1062
            m = FOREIGN_FILE_REFERENCE_OLD.matcher(reference);
1063
            matches = m.matches();
1064
        }
1065
        if (matches) {
810
            String forProjName = m.group(1);
1066
            String forProjName = m.group(1);
811
            String id = m.group(2);
1067
            String id = m.group(2);
812
            removeReference(forProjName, id, true);
1068
            return removeReference(forProjName, id, true, reference.substring(2, reference.length()-1));
813
            return;
814
        }
1069
        }
815
        m = FOREIGN_PLAIN_FILE_REFERENCE.matcher(reference);
1070
        m = FOREIGN_PLAIN_FILE_REFERENCE.matcher(reference);
816
        if (m.matches()) {
1071
        if (m.matches()) {
817
            removeReference(reference);
1072
            return removeFileReference(reference);
818
            return;
819
        }
1073
        }
1074
        return false;
820
    }
1075
    }
821
    
1076
    
822
    /**
1077
    /**
Lines 847-856 Link Here
847
        
1102
        
848
        private final String foreignProjectName;
1103
        private final String foreignProjectName;
849
        private final String artifactType;
1104
        private final String artifactType;
850
        private final URI scriptLocation;
1105
        private URI scriptLocation;
1106
        // introduced in /2 version
1107
        private String newScriptLocation;
851
        private final String targetName;
1108
        private final String targetName;
852
        private final String cleanTargetName;
1109
        private final String cleanTargetName;
853
        private final String artifactID;
1110
        private final String artifactID;
1111
        private final Properties props;
854
        
1112
        
855
        /**
1113
        /**
856
         * Create a raw reference descriptor.
1114
         * Create a raw reference descriptor.
Lines 864-878 Link Here
864
         * @throws IllegalArgumentException if the script location is given an absolute URI
1122
         * @throws IllegalArgumentException if the script location is given an absolute URI
865
         */
1123
         */
866
        public RawReference(String foreignProjectName, String artifactType, URI scriptLocation, String targetName, String cleanTargetName, String artifactID) throws IllegalArgumentException {
1124
        public RawReference(String foreignProjectName, String artifactType, URI scriptLocation, String targetName, String cleanTargetName, String artifactID) throws IllegalArgumentException {
1125
           this(foreignProjectName, artifactType, scriptLocation, null, targetName, cleanTargetName, artifactID, new Properties());
1126
        }
1127
        
1128
        /**
1129
         * Create a raw reference descriptor.
1130
         * As this is basically just a struct, does no real work.
1131
         * @param foreignProjectName the name of the foreign project (usually its code name)
1132
         * @param artifactType the {@link AntArtifact#getType type} of the build artifact
1133
         * @param newScriptLocation absolute path to the build script; can contain Ant-like properties
1134
         * @param targetName the Ant target name
1135
         * @param cleanTargetName the Ant clean target name
1136
         * @param artifactID the {@link AntArtifact#getID ID} of the build artifact
1137
         * @param props optional properties to be used for target execution; never null
1138
         * @throws IllegalArgumentException if the script location is given an absolute URI
1139
         * @since X.XX
1140
         */
1141
        public RawReference(String foreignProjectName, String artifactType, String newScriptLocation, String targetName, String cleanTargetName, String artifactID, Properties props) throws IllegalArgumentException {
1142
           this(foreignProjectName, artifactType, null, newScriptLocation, targetName, cleanTargetName, artifactID, props);
1143
        }
1144
        
1145
        private RawReference(String foreignProjectName, String artifactType, URI scriptLocation, String newScriptLocation, String targetName, String cleanTargetName, String artifactID, Properties props) throws IllegalArgumentException {
867
            this.foreignProjectName = foreignProjectName;
1146
            this.foreignProjectName = foreignProjectName;
868
            this.artifactType = artifactType;
1147
            this.artifactType = artifactType;
869
            if (scriptLocation.isAbsolute()) {
1148
            if (scriptLocation != null && scriptLocation.isAbsolute()) {
870
                throw new IllegalArgumentException("Cannot use an absolute URI " + scriptLocation + " for script location"); // NOI18N
1149
                throw new IllegalArgumentException("Cannot use an absolute URI " + scriptLocation + " for script location"); // NOI18N
871
            }
1150
            }
872
            this.scriptLocation = scriptLocation;
1151
            this.scriptLocation = scriptLocation;
1152
            this.newScriptLocation = newScriptLocation;
873
            this.targetName = targetName;
1153
            this.targetName = targetName;
874
            this.cleanTargetName = cleanTargetName;
1154
            this.cleanTargetName = cleanTargetName;
875
            this.artifactID = artifactID;
1155
            this.artifactID = artifactID;
1156
            this.props = props;
876
        }
1157
        }
877
        
1158
        
878
        private static final List/*<String>*/ SUB_ELEMENT_NAMES = Arrays.asList(new String[] {
1159
        private static final List/*<String>*/ SUB_ELEMENT_NAMES = Arrays.asList(new String[] {
Lines 889-894 Link Here
889
         * @throws IllegalArgumentException if anything is missing or duplicated or malformed etc.
1170
         * @throws IllegalArgumentException if anything is missing or duplicated or malformed etc.
890
         */
1171
         */
891
        static RawReference create(Element xml) throws IllegalArgumentException {
1172
        static RawReference create(Element xml) throws IllegalArgumentException {
1173
            if (REFS_NS.equals(xml.getNamespaceURI())) {
1174
                return create1(xml);
1175
            } else {
1176
                return create2(xml);
1177
            }
1178
        }
1179
        
1180
        private static RawReference create1(Element xml) throws IllegalArgumentException {
892
            if (!REF_NAME.equals(xml.getLocalName()) || !REFS_NS.equals(xml.getNamespaceURI())) {
1181
            if (!REF_NAME.equals(xml.getLocalName()) || !REFS_NS.equals(xml.getNamespaceURI())) {
893
                throw new IllegalArgumentException("bad element name: " + xml); // NOI18N
1182
                throw new IllegalArgumentException("bad element name: " + xml); // NOI18N
894
            }
1183
            }
Lines 921-947 Link Here
921
            return new RawReference(values[0], values[1], scriptLocation, values[3], values[4], values[5]);
1210
            return new RawReference(values[0], values[1], scriptLocation, values[3], values[4], values[5]);
922
        }
1211
        }
923
        
1212
        
1213
        private static RawReference create2(Element xml) throws IllegalArgumentException {
1214
            if (!REF_NAME.equals(xml.getLocalName()) || !REFS_NS2.equals(xml.getNamespaceURI())) {
1215
                throw new IllegalArgumentException("bad element name: " + xml); // NOI18N
1216
            }
1217
            List nl = Util.findSubElements(xml);
1218
            if (nl.size() < 6) {
1219
                throw new IllegalArgumentException("missing or extra data: " + xml); // NOI18N
1220
            }
1221
            String[] values = new String[6];
1222
            for (int i = 0; i < 6; i++) {
1223
                Element el = (Element)nl.get(i);
1224
                if (!REFS_NS2.equals(el.getNamespaceURI())) {
1225
                    throw new IllegalArgumentException("bad subelement ns: " + el); // NOI18N
1226
                }
1227
                String elName = el.getLocalName();
1228
                int idx = SUB_ELEMENT_NAMES.indexOf(elName);
1229
                if (idx == -1) {
1230
                    throw new IllegalArgumentException("bad subelement name: " + elName); // NOI18N
1231
                }
1232
                String val = Util.findText(el);
1233
                if (val == null) {
1234
                    throw new IllegalArgumentException("empty subelement: " + el); // NOI18N
1235
                }
1236
                if (values[idx] != null) {
1237
                    throw new IllegalArgumentException("duplicate " + elName + ": " + values[idx] + " and " + val); // NOI18N
1238
                }
1239
                values[idx] = val;
1240
            }
1241
            Properties props = new Properties();
1242
            if (nl.size() == 7) {
1243
                Element el = (Element)nl.get(6);
1244
                if (!REFS_NS2.equals(el.getNamespaceURI())) {
1245
                    throw new IllegalArgumentException("bad subelement ns: " + el); // NOI18N
1246
                }
1247
                if (!"properties".equals(el.getLocalName())) { // NOI18N
1248
                    throw new IllegalArgumentException("bad subelement. expected 'properties': " + el); // NOI18N
1249
                }
1250
                Iterator it = Util.findSubElements(el).iterator();
1251
                while (it.hasNext()) {
1252
                    el = (Element)it.next();
1253
                    String key = el.getAttribute("name");
1254
                    String value = Util.findText(el);
1255
                    props.setProperty(key, value);
1256
                }
1257
            }
1258
            assert !Arrays.asList(values).contains(null);
1259
            return new RawReference(values[0], values[1], values[2], values[3], values[4], values[5], props);
1260
        }
1261
        
924
        /**
1262
        /**
925
         * Write a RawReference as an XML &lt;reference&gt; fragment.
1263
         * Write a RawReference as an XML &lt;reference&gt; fragment.
926
         */
1264
         */
927
        Element toXml(Document ownerDocument) {
1265
        Element toXml(String namespace, Document ownerDocument) {
928
            Element el = ownerDocument.createElementNS(REFS_NS, REF_NAME);
1266
            Element el = ownerDocument.createElementNS(namespace, REF_NAME);
929
            String[] values = {
1267
            String[] values = {
930
                foreignProjectName,
1268
                foreignProjectName,
931
                artifactType,
1269
                artifactType,
932
                scriptLocation.toString(),
1270
                newScriptLocation != null ? newScriptLocation : scriptLocation.toString(),
933
                targetName,
1271
                targetName,
934
                cleanTargetName,
1272
                cleanTargetName,
935
                artifactID,
1273
                artifactID,
936
            };
1274
            };
937
            for (int i = 0; i < 6; i++) {
1275
            for (int i = 0; i < 6; i++) {
938
                Element subel = ownerDocument.createElementNS(REFS_NS, (String)SUB_ELEMENT_NAMES.get(i));
1276
                Element subel = ownerDocument.createElementNS(namespace, (String)SUB_ELEMENT_NAMES.get(i));
939
                subel.appendChild(ownerDocument.createTextNode(values[i]));
1277
                subel.appendChild(ownerDocument.createTextNode(values[i]));
940
                el.appendChild(subel);
1278
                el.appendChild(subel);
941
            }
1279
            }
1280
            if (props.keySet().size() > 0) {
1281
                assert namespace.equals(REFS_NS2) : "can happen only in /2"; // NOI18N
1282
                Element propEls = ownerDocument.createElementNS(namespace, "properties"); // NOI18N
1283
                el.appendChild(propEls);
1284
                List keys = new ArrayList(props.keySet());
1285
                Collections.sort(keys);
1286
                Iterator it = keys.iterator();
1287
                while (it.hasNext()) {
1288
                    String key = (String)it.next();
1289
                    Element propEl = ownerDocument.createElementNS(namespace, "property"); // NOI18N
1290
                    propEl.appendChild(ownerDocument.createTextNode(props.getProperty(key)));
1291
                    propEl.setAttribute("name", key); // NOI18N
1292
                    propEls.appendChild(propEl);
1293
                }
1294
            }
942
            return el;
1295
            return el;
943
        }
1296
        }
944
        
1297
        
1298
        private String getNS() {
1299
            if (newScriptLocation != null) {
1300
                return REFS_NS2;
1301
            } else {
1302
                return REFS_NS;
1303
            }
1304
        }
1305
        
945
        /**
1306
        /**
946
         * Get the name of the foreign project as referred to from this project.
1307
         * Get the name of the foreign project as referred to from this project.
947
         * Usually this will be the code name of the foreign project, but it may
1308
         * Usually this will be the code name of the foreign project, but it may
Lines 968-979 Link Here
968
         * project directory.
1329
         * project directory.
969
         * This is the script which would be called to build the desired artifact.
1330
         * This is the script which would be called to build the desired artifact.
970
         * @return the script location
1331
         * @return the script location
1332
         * @deprecated use {@link #getScriptLocationValue} instead; may return null now
971
         */
1333
         */
972
        public URI getScriptLocation() {
1334
        public URI getScriptLocation() {
973
            return scriptLocation;
1335
            return scriptLocation;
974
        }
1336
        }
975
        
1337
        
976
        /**
1338
        /**
1339
         * Get absolute path location of the foreign project's build script.
1340
         * This is the script which would be called to build the desired artifact.
1341
         * @return absolute path possibly containing Ant properties
1342
         */
1343
        public String getScriptLocationValue() {
1344
            if (newScriptLocation != null) {
1345
                return newScriptLocation;
1346
            } else {
1347
                return "${project."+foreignProjectName+"}/"+scriptLocation.toString();
1348
            }
1349
        }
1350
        
1351
        /**
977
         * Get the Ant target name to build the artifact.
1352
         * Get the Ant target name to build the artifact.
978
         * @return the target name
1353
         * @return the target name
979
         */
1354
         */
Lines 998-1003 Link Here
998
            return artifactID;
1373
            return artifactID;
999
        }
1374
        }
1000
        
1375
        
1376
        public Properties getProperties() {
1377
            return props;
1378
        }
1379
        
1001
        /**
1380
        /**
1002
         * Attempt to convert this reference to a live artifact object.
1381
         * Attempt to convert this reference to a live artifact object.
1003
         * This involves finding the referenced foreign project on disk
1382
         * This involves finding the referenced foreign project on disk
Lines 1046-1053 Link Here
1046
            });
1425
            });
1047
        }
1426
        }
1048
        
1427
        
1428
        private void upgrade() {
1429
            assert newScriptLocation == null && scriptLocation != null : "was already upgraded "+this;
1430
            newScriptLocation = "${project."+foreignProjectName+"}/" +scriptLocation.toString(); // NOI18N
1431
            scriptLocation = null;
1432
        }
1433
        
1049
        public String toString() {
1434
        public String toString() {
1050
            return "ReferenceHelper.RawReference<" + foreignProjectName + "," + artifactType + "," + scriptLocation + "," + targetName + "," + cleanTargetName + "," + artifactID + ">"; // NOI18N
1435
            return "ReferenceHelper.RawReference<" + foreignProjectName + "," + 
1436
                artifactType + "," + newScriptLocation != null ? newScriptLocation : scriptLocation + 
1437
                "," + targetName + "," + cleanTargetName + "," + artifactID + ">"; // NOI18N
1051
        }
1438
        }
1052
        
1439
        
1053
    }
1440
    }

Return to bug 52886