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 44035
Collapse All | Expand All

(-)ant/project/apichanges.xml:1.22 (-1 / +38 lines)
Lines 104-110 Link Here
104
    <!-- ACTUAL CHANGES BEGIN HERE: -->
104
    <!-- ACTUAL CHANGES BEGIN HERE: -->
105
105
106
    <changes>
106
    <changes>
107
        
107
108
        <change id="ant-project-libraries">
109
            <api name="general"/>
110
            <summary>Support for project-specific libraries</summary>
111
            <version major="1" minor="19"/>
112
            <date day="22" month="1" year="2008"/>
113
            <author login="jglick"/>
114
            <compatibility addition="yes"/>
115
            <description>
116
                <p>
117
                    A new XML schema
118
                    (<a href="http://www.netbeans.org/ns/ant-project-libraries/1.xsd"><code>http://www.netbeans.org/ns/ant-project-libraries/1</code></a>),
119
                    several new API methods
120
                    (<code>AntProjectHelper.getProjectLibrariesPropertyProvider</code>,
121
                    <code>AntProjectHelper.isSharableProject</code>,
122
                    <code>AntProjectHelper.getLibrariesLocation</code>,
123
                    <code>AntProjectHelper.setLibrariesLocation</code>,
124
                    <code>ProjectGenerator.createProject(FileObject, String, String)</code>,
125
                    <code>ReferenceHelper.createForeignFileReferenceAsIs</code>,
126
                    <code>ReferenceHelper.createExtraForeignFileReferenceAsIs</code>,
127
                    <code>ReferenceHelper.copyLibrary</code>,
128
                    <code>ReferenceHelper.getLibraryChooserImportHandler</code>,
129
                    <code>ReferenceHelper.createLibraryReference</code>,
130
                    <code>ReferenceHelper.getProjectLibraryManager</code>,
131
                    <code>ReferenceHelper.findLibrary</code> and
132
                    <code>FileChooser.showRelativizeFilePathCustomizer</code>) and
133
                    custom file chooser (<code>FileChooser</code>)
134
                    provide support for collocated library definitions
135
                    referred to from Ant-based projects.
136
                </p>
137
            </description>
138
            <class package="org.netbeans.spi.project.ant" name="AntProjectHelper"/>
139
            <class package="org.netbeans.spi.project.ant" name="ReferenceHelper"/>
140
            <class package="org.netbeans.spi.project.support.ant" name="ProjectGenerator"/>
141
            <class package="org.netbeans.api.project.ant" name="FileChooser"/>
142
            <issue number="44035"/>
143
        </change>
144
108
        <change id="owned-files">
145
        <change id="owned-files">
109
            <api name="general"/>
146
            <api name="general"/>
110
            <summary>Support for adding external files owned by project</summary>
147
            <summary>Support for adding external files owned by project</summary>
(-)ant/project/manifest.mf:1.22 (-1 / +1 lines)
Lines 1-6 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.modules.project.ant/1
2
OpenIDE-Module: org.netbeans.modules.project.ant/1
3
OpenIDE-Module-Specification-Version: 1.18
3
OpenIDE-Module-Specification-Version: 1.19
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/project/ant/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/project/ant/Bundle.properties
5
OpenIDE-Module-Install: org/netbeans/modules/project/ant/AntProjectModule.class
5
OpenIDE-Module-Install: org/netbeans/modules/project/ant/AntProjectModule.class
6
6
(-)ant/project/nbproject/project.xml:1.25 (+18 lines)
Lines 47-57 Link Here
47
            <code-name-base>org.netbeans.modules.project.ant</code-name-base>
47
            <code-name-base>org.netbeans.modules.project.ant</code-name-base>
48
            <module-dependencies>
48
            <module-dependencies>
49
                <dependency>
49
                <dependency>
50
                    <code-name-base>org.jdesktop.layout</code-name-base>
51
                    <build-prerequisite/>
52
                    <compile-dependency/>
53
                    <run-dependency>
54
                        <release-version>1</release-version>
55
                        <specification-version>1.5</specification-version>
56
                    </run-dependency>
57
                </dependency>
58
                <dependency>
50
                    <code-name-base>org.netbeans.api.progress</code-name-base>
59
                    <code-name-base>org.netbeans.api.progress</code-name-base>
51
                    <build-prerequisite/>
60
                    <build-prerequisite/>
52
                    <compile-dependency/>
61
                    <compile-dependency/>
53
                    <run-dependency>
62
                    <run-dependency>
54
                        <release-version>1</release-version>
63
                        <release-version>1</release-version>
64
                    </run-dependency>
65
                </dependency>
66
                <dependency>
67
                    <code-name-base>org.netbeans.modules.project.libraries</code-name-base>
68
                    <build-prerequisite/>
69
                    <compile-dependency/>
70
                    <run-dependency>
71
                        <release-version>1</release-version>
72
                        <specification-version>1.15</specification-version>
55
                    </run-dependency>
73
                    </run-dependency>
56
                </dependency>
74
                </dependency>
57
                <dependency>
75
                <dependency>
(-)/dev/null (+1 lines)
Added Link Here
1
org.netbeans.modules.project.ant.ProjectLibraryProvider
(-)/dev/null (+2 lines)
Added Link Here
1
org.netbeans.modules.project.ant.ProjectLibraryProvider$SharabilityQueryImpl
2
#position=50
(-)/dev/null (+216 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * Contributor(s):
25
 *
26
 * The Original Software is NetBeans. The Initial Developer of the Original
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
 * Microsystems, Inc. All Rights Reserved.
29
 *
30
 * If you wish your version of this file to be governed by only the CDDL
31
 * or only the GPL Version 2, indicate your decision by adding
32
 * "[Contributor] elects to include this software in this distribution
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
34
 * single choice of license, a recipient has the option to distribute
35
 * your version of this file under either the CDDL, the GPL Version 2 or
36
 * to extend the choice of license to its licensees as provided above.
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
38
 * Version 2 license, then the option applies only if the new code is
39
 * made subject to such option by the copyright holder.
40
 */
41
42
package org.netbeans.api.project.ant;
43
44
import java.awt.Dialog;
45
import java.io.File;
46
import java.io.IOException;
47
import java.net.URI;
48
import java.net.URL;
49
import javax.swing.JFileChooser;
50
import org.netbeans.api.project.FileOwnerQuery;
51
import org.netbeans.api.project.Project;
52
import org.netbeans.api.project.libraries.LibraryManager;
53
import org.netbeans.modules.project.ant.FileChooserAccessory;
54
import org.netbeans.modules.project.ant.RelativizeFilePathCustomizer;
55
import org.netbeans.modules.project.ant.ProjectLibraryProvider;
56
import org.netbeans.spi.project.AuxiliaryConfiguration;
57
import org.netbeans.spi.project.support.ant.AntProjectHelper;
58
import org.openide.DialogDescriptor;
59
import org.openide.DialogDisplayer;
60
import org.openide.filesystems.FileObject;
61
import org.openide.filesystems.FileUtil;
62
import org.openide.util.NbBundle;
63
64
65
/**
66
 * Custom file chooser allowing user to choose how a file will be referenced 
67
 * from a project - via absolute path, or relative path. Make sure you call
68
 * {@link #getFiles} instead of {@link #getSelectedFiles} as it returns relative
69
 * files and performs file copying if necessary.
70
 * 
71
 * @author David Konecny
72
 * @since org.netbeans.modules.project.ant/1 1.19
73
 */
74
public final class FileChooser extends JFileChooser {
75
76
    private FileChooserAccessory accessory;
77
78
    /**
79
     * Create chooser for given AntProjectHelper. Standard file chooser is shown
80
     * if project is not sharable.
81
     * 
82
     * @param helper ant project helper; cannot be null
83
     * @param copyAllowed is file copying allowed
84
     */
85
    public FileChooser(AntProjectHelper helper, boolean copyAllowed) {
86
        super();
87
        LibraryManager lm = ProjectLibraryProvider.getProjectLibraryManager(helper);
88
        if (lm != null) {
89
            URL u = lm.getLocation();
90
            if (u != null) {
91
                File libBase = new File(URI.create(u.toExternalForm())).getParentFile();
92
                accessory = new FileChooserAccessory(this, FileUtil.toFile(helper.getProjectDirectory()), 
93
                    libBase, copyAllowed);
94
                setAccessory(accessory);
95
            }
96
        }
97
    }
98
99
    /**
100
     * Create chooser for given base folder and shared libraries folder. 
101
     * 
102
     * @param baseFolder base folder to which all selected files will be relativized;
103
     *  can be null in which case regular file chooser is shown
104
     * @param sharedLibrariesFolder providing shared libraries folder enables option
105
     *  of copying selected files there; can be null in which case copying option
106
     *  is disabled
107
     */
108
    public FileChooser(File baseFolder, File sharedLibrariesFolder) {
109
        super();
110
        if (baseFolder != null) {
111
            accessory = new FileChooserAccessory(this, baseFolder, sharedLibrariesFolder, sharedLibrariesFolder != null);
112
            setAccessory(accessory);
113
        }
114
    }
115
    
116
    /**
117
     * Returns array of files selected. The difference from 
118
     * {@link #getSelectedFiles} is that depends on user's choice the files
119
     * may be relative and they may have been copied to different location.
120
     * 
121
     * @return array of files which may be relative to base folder this chooser
122
     *  was created for; e.g. project folder in case of AntProjectHelper 
123
     *  constructor; never null; can be empty array
124
     * @throws java.io.IOException any IO problem; for example during 
125
     *  file copying
126
     */
127
    public File[] getFiles() throws IOException {
128
        if (accessory != null) {
129
            accessory.copyFilesIfNecessary();
130
            if (accessory.isRelative()) {
131
                return accessory.getFiles();
132
            }
133
        }
134
        if (isMultiSelectionEnabled()) {
135
            return getSelectedFiles();
136
        } else {
137
            if (getSelectedFile() != null) {
138
                return new File[]{getSelectedFile()};
139
            } else {
140
                return new File[0];
141
            }
142
        }
143
    }
144
145
    @Override
146
    public void approveSelection() {
147
        if (accessory != null && !accessory.canApprove()) {
148
            return;
149
        }
150
        super.approveSelection();
151
    }
152
153
    /**
154
     * Show UI allowing user to decide how the given file should be referenced,
155
     * that is via absolute or relative path. Optionally UI can allow to copy
156
     * file to shared libraries folder.
157
     * 
158
     * @param fileToReference file in question
159
     * @param projectArtifact any project artifact, e.g. project folder or project source etc.
160
     * @param copyAllowed is file copying allowed
161
     * @return possibly relative file; null if cancelled by user or project 
162
     *  cannot be found for project artifact
163
     * @throws java.io.IOException any IO failure during file copying
164
     */
165
    public static File showRelativizeFilePathCustomizer(File fileToReference, FileObject projectArtifact, 
166
            boolean copyAllowed) throws IOException {
167
        Project p = FileOwnerQuery.getOwner(projectArtifact);
168
        if (p == null) {
169
            return null;
170
        }
171
        AuxiliaryConfiguration aux = p.getLookup().lookup(AuxiliaryConfiguration.class);
172
        assert aux != null : projectArtifact;
173
        if (aux == null) {
174
            return null;
175
        }
176
        File projFolder = FileUtil.toFile(p.getProjectDirectory());
177
        File libBase = ProjectLibraryProvider.getLibrariesLocation(aux, projFolder);
178
        if (libBase != null) {
179
            libBase = libBase.getParentFile();
180
        }
181
        return showRelativizeFilePathCustomizer(fileToReference, projFolder, libBase, copyAllowed);
182
    }
183
184
    /**
185
     * Show UI allowing user to decide how the given file should be referenced,
186
     * that is via absolute or relative path. Optionally UI can allow to copy
187
     * file to shared libraries folder.
188
     * 
189
     * @param fileToReference file in question
190
     * @param baseFolder folder to relativize file against
191
     * @param sharedLibrariesFolder optional shared libraries folder; can be null;
192
     *  if provided UI will allow user to copy given file there
193
     * @param copyAllowed is file copying allowed
194
     * @return possibly relative file; null if cancelled by user
195
     * @throws java.io.IOException any IO failure during file copying
196
     */
197
    private static File showRelativizeFilePathCustomizer(File fileToReference, File baseFolder, 
198
            File sharedLibrariesFolder, boolean copyAllowed) throws IOException {
199
        RelativizeFilePathCustomizer panel = new RelativizeFilePathCustomizer(
200
            fileToReference, baseFolder, sharedLibrariesFolder, copyAllowed);
201
        DialogDescriptor descriptor = new DialogDescriptor (panel,
202
            NbBundle.getMessage(RelativizeFilePathCustomizer.class, "RelativizeFilePathCustomizer.title"));
203
        Dialog dlg = DialogDisplayer.getDefault().createDialog(descriptor);
204
        try {
205
            dlg.setVisible(true);
206
            if (descriptor.getValue() == DialogDescriptor.OK_OPTION) {
207
                return panel.getFile();
208
            } else {
209
                return null;
210
            }
211
        } finally {
212
            dlg.dispose();
213
        }
214
    }
215
216
}
(-)ant/project/src/org/netbeans/modules/project/ant/Bundle.properties:1.12 (+12 lines)
Lines 52-54 Link Here
52
AntBasedProjectFactorySingleton.parseError=Error parsing {0}: {1}
52
AntBasedProjectFactorySingleton.parseError=Error parsing {0}: {1}
53
53
54
LBL_Incompatible_Xalan=An incompatible Xalan version has been detected on the IDE's classpath. The IDE cannot start. Please see http://wiki.netbeans.org/wiki/view/FaqXalanOnCP for more information.
54
LBL_Incompatible_Xalan=An incompatible Xalan version has been detected on the IDE's classpath. The IDE cannot start. Please see http://wiki.netbeans.org/wiki/view/FaqXalanOnCP for more information.
55
56
ProjectLibraryProvider.open_or_create=Open or Create
57
ProjectLibraryProvider.properties_files=*.properties Files
58
FileChooserAccessory.rbRelative.text=Use relative path:
59
FileChooserAccessory.rbCopy.text=Copy to shared libraries location:
60
FileChooserAccessory.rbAbsolute.text=Use absolute path:
61
FileChooserAccessory.warning1=Files can be only copied to the sharable libraries location ({0}) or its subfolder(s).
62
FileChooserAccessory.warning2=Are you sure you want to create folder {0} ?
63
FileChooserAccessory.warning3=Some of the files already exist in destination directory. Are you sure you want to override them?
64
FileChooserAccessory.warning4=Some of the selected files are folders. Are you sure you want to copy folder and recursively all its subfoders?
65
RelativizeFilePathCustomizer.title=Adding artifact to project
66
RelativizeFilePathCustomizer.jLabel1.text=How to reference file {0}?
(-)/dev/null (+121 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8" ?>
2
3
<Form version="1.5" maxVersion="1.5" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
4
  <NonVisualComponents>
5
    <Component class="javax.swing.ButtonGroup" name="buttonGroup1">
6
    </Component>
7
  </NonVisualComponents>
8
  <Properties>
9
    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
10
      <Dimension value="[250, 139]"/>
11
    </Property>
12
    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
13
      <Dimension value="[250, 139]"/>
14
    </Property>
15
  </Properties>
16
  <AuxValues>
17
    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
18
    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
19
    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
20
    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
21
    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
22
    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
23
    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
24
    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
25
  </AuxValues>
26
27
  <Layout>
28
    <DimensionLayout dim="0">
29
      <Group type="103" groupAlignment="0" attributes="0">
30
          <Group type="102" attributes="0">
31
              <Group type="103" groupAlignment="0" attributes="0">
32
                  <Component id="rbRelative" alignment="0" min="-2" max="-2" attributes="0"/>
33
                  <Group type="102" alignment="0" attributes="0">
34
                      <EmptySpace min="21" pref="21" max="21" attributes="0"/>
35
                      <Component id="relativePath" pref="219" max="32767" attributes="0"/>
36
                  </Group>
37
                  <Component id="rbCopy" alignment="0" pref="240" max="32767" attributes="0"/>
38
                  <Group type="102" alignment="0" attributes="0">
39
                      <EmptySpace min="21" pref="21" max="21" attributes="0"/>
40
                      <Component id="copyTo" pref="219" max="32767" attributes="0"/>
41
                  </Group>
42
                  <Component id="rbAbsolute" alignment="0" min="-2" max="-2" attributes="0"/>
43
                  <Group type="102" alignment="0" attributes="0">
44
                      <EmptySpace min="21" pref="21" max="21" attributes="0"/>
45
                      <Component id="absolutePath" pref="219" max="32767" attributes="0"/>
46
                  </Group>
47
              </Group>
48
              <EmptySpace max="-2" attributes="0"/>
49
          </Group>
50
      </Group>
51
    </DimensionLayout>
52
    <DimensionLayout dim="1">
53
      <Group type="103" groupAlignment="0" attributes="0">
54
          <Group type="102" alignment="0" attributes="0">
55
              <Component id="rbRelative" min="-2" max="-2" attributes="0"/>
56
              <EmptySpace max="-2" attributes="0"/>
57
              <Component id="relativePath" min="-2" max="-2" attributes="0"/>
58
              <EmptySpace max="-2" attributes="0"/>
59
              <Component id="rbCopy" min="-2" max="-2" attributes="0"/>
60
              <EmptySpace max="-2" attributes="0"/>
61
              <Component id="copyTo" min="-2" max="-2" attributes="0"/>
62
              <EmptySpace max="-2" attributes="0"/>
63
              <Component id="rbAbsolute" min="-2" max="-2" attributes="0"/>
64
              <EmptySpace max="-2" attributes="0"/>
65
              <Component id="absolutePath" min="-2" max="-2" attributes="0"/>
66
          </Group>
67
      </Group>
68
    </DimensionLayout>
69
  </Layout>
70
  <SubComponents>
71
    <Component class="javax.swing.JRadioButton" name="rbRelative">
72
      <Properties>
73
        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
74
          <ComponentRef name="buttonGroup1"/>
75
        </Property>
76
        <Property name="selected" type="boolean" value="true"/>
77
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
78
          <ResourceString bundle="org/netbeans/modules/project/ant/Bundle.properties" key="FileChooserAccessory.rbRelative.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
79
        </Property>
80
      </Properties>
81
    </Component>
82
    <Component class="javax.swing.JTextField" name="relativePath">
83
      <Properties>
84
        <Property name="editable" type="boolean" value="false"/>
85
        <Property name="text" type="java.lang.String" value="null"/>
86
      </Properties>
87
    </Component>
88
    <Component class="javax.swing.JRadioButton" name="rbCopy">
89
      <Properties>
90
        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
91
          <ComponentRef name="buttonGroup1"/>
92
        </Property>
93
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
94
          <ResourceString bundle="org/netbeans/modules/project/ant/Bundle.properties" key="FileChooserAccessory.rbCopy.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
95
        </Property>
96
      </Properties>
97
    </Component>
98
    <Component class="javax.swing.JTextField" name="copyTo">
99
      <Properties>
100
        <Property name="editable" type="boolean" value="false"/>
101
        <Property name="text" type="java.lang.String" value="null"/>
102
      </Properties>
103
    </Component>
104
    <Component class="javax.swing.JRadioButton" name="rbAbsolute">
105
      <Properties>
106
        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
107
          <ComponentRef name="buttonGroup1"/>
108
        </Property>
109
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
110
          <ResourceString bundle="org/netbeans/modules/project/ant/Bundle.properties" key="FileChooserAccessory.rbAbsolute.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
111
        </Property>
112
      </Properties>
113
    </Component>
114
    <Component class="javax.swing.JTextField" name="absolutePath">
115
      <Properties>
116
        <Property name="editable" type="boolean" value="false"/>
117
        <Property name="text" type="java.lang.String" value="null"/>
118
      </Properties>
119
    </Component>
120
  </SubComponents>
121
</Form>
(-)/dev/null (+417 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * Contributor(s):
25
 *
26
 * The Original Software is NetBeans. The Initial Developer of the Original
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
 * Microsystems, Inc. All Rights Reserved.
29
 *
30
 * If you wish your version of this file to be governed by only the CDDL
31
 * or only the GPL Version 2, indicate your decision by adding
32
 * "[Contributor] elects to include this software in this distribution
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
34
 * single choice of license, a recipient has the option to distribute
35
 * your version of this file under either the CDDL, the GPL Version 2 or
36
 * to extend the choice of license to its licensees as provided above.
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
38
 * Version 2 license, then the option applies only if the new code is
39
 * made subject to such option by the copyright holder.
40
 */
41
42
package org.netbeans.modules.project.ant;
43
44
import java.awt.event.ActionEvent;
45
import java.awt.event.ActionListener;
46
import java.beans.PropertyChangeEvent;
47
import java.beans.PropertyChangeListener;
48
import java.io.File;
49
import java.io.IOException;
50
import java.text.MessageFormat;
51
import java.util.ArrayList;
52
import java.util.Arrays;
53
import java.util.Collections;
54
import java.util.List;
55
import javax.swing.JFileChooser;
56
import org.netbeans.spi.project.support.ant.PropertyUtils;
57
import org.openide.DialogDisplayer;
58
import org.openide.NotifyDescriptor;
59
import org.openide.filesystems.FileObject;
60
import org.openide.filesystems.FileUtil;
61
import org.openide.util.NbBundle;
62
63
/**
64
 * Accessory allowing to choose how file is referenced from a project - relative
65
 * or absolute.
66
 * 
67
 * <p>The panel is used from two different places - FileChooser and 
68
 * RelativizeFilePathCustomizer.
69
 * 
70
 * @author David Konecny
71
 */
72
public class FileChooserAccessory extends javax.swing.JPanel 
73
        implements ActionListener, PropertyChangeListener {
74
75
    private File baseFolder;
76
    private final File sharedLibrariesFolder;
77
    private boolean copyAllowed;
78
    private JFileChooser chooser;
79
    private List<File> copiedRelativeFiles = null;
80
    /** In RelativizeFilePathCustomizer scenario this property holds preselected file */
81
    private File usetThisFileInsteadOfOneFromChooser = null;
82
83
    /**
84
     * Constructor for usage from RelativizeFilePathCustomizer.
85
     */
86
    public FileChooserAccessory(File baseFolder, File sharedLibrariesFolder, boolean copyAllowed, File selectedFile) {
87
        this(null, baseFolder, sharedLibrariesFolder, copyAllowed);
88
        usetThisFileInsteadOfOneFromChooser = selectedFile;
89
        enableAccessory(true);
90
        update(Collections.singletonList(selectedFile));
91
    }
92
    
93
    /**
94
     * Constructor for usage from FileChooser.
95
     */
96
    public FileChooserAccessory(JFileChooser chooser, File baseFolder, File sharedLibrariesFolder, boolean copyAllowed) {
97
        assert baseFolder != null;
98
        assert !baseFolder.isFile();
99
        if (sharedLibrariesFolder != null) {
100
            assert !sharedLibrariesFolder.isFile() : true;
101
            if (!sharedLibrariesFolder.equals(FileUtil.normalizeFile(sharedLibrariesFolder))) {
102
                throw new IllegalArgumentException("Parameter file was not "+  // NOI18N
103
                    "normalized. Was "+sharedLibrariesFolder+" instead of "+
104
                    FileUtil.normalizeFile(sharedLibrariesFolder));  // NOI18N
105
            }
106
        }
107
        this.baseFolder = baseFolder;
108
        this.sharedLibrariesFolder = sharedLibrariesFolder;
109
        this.copyAllowed = copyAllowed && sharedLibrariesFolder != null;
110
        this.chooser = chooser;
111
        initComponents();
112
        //copyPanel.setVisible(copyAllowed);
113
        rbCopy.addActionListener(this);
114
        rbRelative.addActionListener(this);
115
        rbAbsolute.addActionListener(this);
116
        if (chooser != null) {
117
            chooser.addPropertyChangeListener(this);
118
        }
119
        if (sharedLibrariesFolder != null) {
120
            copyTo.setText(sharedLibrariesFolder.getAbsolutePath());
121
        }
122
        enableAccessory(false);
123
    }
124
125
    public File[] getFiles() {
126
        assert isRelative();
127
        if (isCopy()) {
128
            return copiedRelativeFiles.toArray(new File[copiedRelativeFiles.size()]);
129
        } else {
130
            List<File> files = Arrays.asList(getSelectedFiles());
131
            List<File> l = getRelativeFiles(files);
132
            return l.toArray(new File[l.size()]);
133
        }
134
    }
135
    
136
    public boolean canApprove() {
137
        if (!isCopy()) {
138
            return true;
139
        }
140
        File f = FileUtil.normalizeFile(new File(copyTo.getText()));
141
        if (!f.getPath().equals(sharedLibrariesFolder.getPath()) && 
142
                !(f.getPath()).startsWith(sharedLibrariesFolder.getPath()+File.separatorChar)) {
143
            DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
144
                    MessageFormat.format(NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.warning1"), // NOI18N
145
                    new Object[] {sharedLibrariesFolder.getPath()})));
146
                
147
            return false;
148
        }
149
        final File[] files = getSelectedFiles();
150
        if (f.exists()) {
151
            for (File file : files) {
152
                File testFile = new File(f, file.getName());
153
                if (testFile.exists()) {
154
                    if (NotifyDescriptor.YES_OPTION != DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation(
155
                            NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.warning3")))) {
156
                        return false;
157
                    }
158
                    break;
159
                }
160
            }
161
        } else {
162
            if (NotifyDescriptor.YES_OPTION != DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation(
163
                    MessageFormat.format(NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.warning2"), // NOI18N
164
                    new Object[] {f.getPath()})))) {
165
                return false;
166
            }
167
        }
168
        for (File file : files) {
169
            if (file.isDirectory()) {
170
                if (NotifyDescriptor.YES_OPTION != DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation(
171
                        NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.warning4")))) { // NOI18N
172
                    return false;
173
                }
174
                break;
175
            }
176
        }
177
        
178
        return true;
179
    }
180
    
181
    public void copyFilesIfNecessary() throws IOException {
182
        if (!isCopy()) {
183
            return;
184
        }
185
        File f = FileUtil.normalizeFile(new File(copyTo.getText()));
186
        FileUtil.createFolder(f);
187
        FileObject fo = FileUtil.toFileObject(FileUtil.normalizeFile(f));
188
        List<File> selectedFiles = Arrays.asList(getSelectedFiles());
189
        copyFiles(selectedFiles, fo);
190
    }
191
    
192
    private void enableAccessory(boolean enable) {
193
        rbRelative.setEnabled(enable);
194
        rbCopy.setEnabled(enable && copyAllowed);
195
        rbAbsolute.setEnabled(enable);
196
        copyTo.setEnabled(enable);
197
        copyTo.setEditable(enable && rbCopy.isSelected());
198
    }
199
    
200
    private File[] getSelectedFiles() {
201
        if (usetThisFileInsteadOfOneFromChooser != null) {
202
            return new File[]{usetThisFileInsteadOfOneFromChooser};
203
        }
204
        if (chooser.isMultiSelectionEnabled()) {
205
            return chooser.getSelectedFiles();
206
        } else {
207
            if (chooser.getSelectedFile() != null) {
208
                return new File[] { chooser.getSelectedFile() };
209
            } else {
210
                return new File[0];
211
            }
212
        }
213
    }
214
    
215
    public boolean isRelative() {
216
        return (rbRelative.isEnabled() && rbRelative.isSelected()) || isCopy();
217
    }
218
    
219
    private boolean isCopy() {
220
        return rbCopy.isEnabled() && rbCopy.isSelected();
221
    }
222
    
223
    public void actionPerformed(ActionEvent e) {
224
        copyTo.setEditable(e.getSource() == rbCopy);
225
    }
226
227
    public void propertyChange(PropertyChangeEvent e) {
228
        if (!(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(e.getPropertyName()) ||
229
             JFileChooser.SELECTED_FILES_CHANGED_PROPERTY.equals(e.getPropertyName()))) {
230
            return;
231
        }
232
        File[] files = getSelectedFiles();
233
        enableAccessory(files.length != 0);
234
        update(Arrays.asList(files));
235
    }
236
        
237
    private void update(List<File> files) {
238
        StringBuffer absolute = new StringBuffer();
239
        StringBuffer relative = new StringBuffer();
240
        StringBuffer copy = new StringBuffer();
241
        boolean isRelative = true;
242
        for (File file : files) {
243
            if (absolute.length() != 0) {
244
                absolute.append(", ");
245
                relative.append(", ");
246
                if (file.getParentFile() != null && file.getParentFile().getParentFile() != null &&
247
                        file.getParentFile().getName().length() > 0) {
248
                    copy.setLength(0);
249
                    copy.append("/"+file.getParentFile().getName());
250
                }
251
            } else {
252
                // first file:
253
                if (file.getParentFile() != null && file.getParentFile().getParentFile() != null &&
254
                        file.getParentFile().getName().length() > 0) {
255
                    copy.append("/"+file.getParentFile().getName()+"/"+file.getName());
256
                } else {
257
                    copy.append("/");
258
                }
259
            }
260
            absolute.append(file.getAbsolutePath());
261
            String s = PropertyUtils.relativizeFile(baseFolder, file);
262
            if (s == null) {
263
                isRelative = false;
264
            }
265
            relative.append(s);
266
        }
267
        rbRelative.setEnabled(isRelative && rbRelative.isEnabled());
268
        relativePath.setText(relative.toString());
269
        relativePath.setCaretPosition(0);
270
        relativePath.setToolTipText(relative.toString());
271
        if (!isRelative) {
272
            relativePath.setText("");
273
            relativePath.setToolTipText("");
274
        }
275
        absolutePath.setText(absolute.toString());
276
        absolutePath.setCaretPosition(0);
277
        absolutePath.setToolTipText(absolute.toString());
278
    }
279
280
    private List<File> getRelativeFiles(List<File> files) {
281
        List<File> fs = new ArrayList<File>();
282
        for (File file : files) {
283
            String s = PropertyUtils.relativizeFile(baseFolder, file);
284
            if (s != null) {
285
                fs.add(new File(s));
286
            }
287
        }
288
        return fs;
289
    }
290
291
    private void copyFiles(List<File> files, FileObject newRoot) throws IOException {
292
        List<File> fs = new ArrayList<File>();
293
        for (File file : files) {
294
            FileObject fo = FileUtil.toFileObject(file);
295
            FileObject newFO;
296
            if (fo.isFolder()) {
297
                newFO = copyFolderRecursively(fo, newRoot);
298
            } else {
299
                FileObject foExists = newRoot.getFileObject(fo.getName(), fo.getExt());
300
                if (foExists != null) {
301
                    foExists.delete();
302
                }
303
                newFO = FileUtil.copyFile(fo, newRoot, fo.getName(), fo.getExt());
304
            }
305
            fs.add(FileUtil.toFile(newFO));
306
        }
307
        copiedRelativeFiles = getRelativeFiles(fs);
308
    }
309
    
310
    public static FileObject copyFolderRecursively(FileObject sourceFolder, FileObject destination) throws IOException {
311
        assert sourceFolder.isFolder() : sourceFolder;
312
        assert destination.isFolder() : destination;
313
        FileObject destinationSubFolder = destination.getFileObject(sourceFolder.getName());
314
        if (destinationSubFolder == null) {
315
            destinationSubFolder = destination.createFolder(sourceFolder.getName());
316
        }
317
        for (FileObject fo : sourceFolder.getChildren()) {
318
            if (fo.isFolder()) {
319
                copyFolderRecursively(fo, destinationSubFolder);
320
            } else {
321
                FileObject foExists = destinationSubFolder.getFileObject(fo.getName(), fo.getExt());
322
                if (foExists != null) {
323
                    foExists.delete();
324
                }
325
                FileObject newFO = FileUtil.copyFile(fo, destinationSubFolder, fo.getName(), fo.getExt());
326
            }
327
        }
328
        return destinationSubFolder;
329
    }
330
331
    /** This method is called from within the constructor to
332
     * initialize the form.
333
     * WARNING: Do NOT modify this code. The content of this method is
334
     * always regenerated by the Form Editor.
335
     */
336
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
337
    private void initComponents() {
338
339
        buttonGroup1 = new javax.swing.ButtonGroup();
340
        rbRelative = new javax.swing.JRadioButton();
341
        relativePath = new javax.swing.JTextField();
342
        rbCopy = new javax.swing.JRadioButton();
343
        copyTo = new javax.swing.JTextField();
344
        rbAbsolute = new javax.swing.JRadioButton();
345
        absolutePath = new javax.swing.JTextField();
346
347
        setMinimumSize(new java.awt.Dimension(250, 139));
348
        setPreferredSize(new java.awt.Dimension(250, 139));
349
350
        buttonGroup1.add(rbRelative);
351
        rbRelative.setSelected(true);
352
        rbRelative.setText(org.openide.util.NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.rbRelative.text")); // NOI18N
353
354
        relativePath.setEditable(false);
355
        relativePath.setText(null);
356
357
        buttonGroup1.add(rbCopy);
358
        rbCopy.setText(org.openide.util.NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.rbCopy.text")); // NOI18N
359
360
        copyTo.setEditable(false);
361
        copyTo.setText(null);
362
363
        buttonGroup1.add(rbAbsolute);
364
        rbAbsolute.setText(org.openide.util.NbBundle.getMessage(FileChooserAccessory.class, "FileChooserAccessory.rbAbsolute.text")); // NOI18N
365
366
        absolutePath.setEditable(false);
367
        absolutePath.setText(null);
368
369
        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
370
        this.setLayout(layout);
371
        layout.setHorizontalGroup(
372
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
373
            .add(layout.createSequentialGroup()
374
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
375
                    .add(rbRelative)
376
                    .add(layout.createSequentialGroup()
377
                        .add(21, 21, 21)
378
                        .add(relativePath, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 219, Short.MAX_VALUE))
379
                    .add(rbCopy, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 240, Short.MAX_VALUE)
380
                    .add(layout.createSequentialGroup()
381
                        .add(21, 21, 21)
382
                        .add(copyTo, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 219, Short.MAX_VALUE))
383
                    .add(rbAbsolute)
384
                    .add(layout.createSequentialGroup()
385
                        .add(21, 21, 21)
386
                        .add(absolutePath, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 219, Short.MAX_VALUE)))
387
                .addContainerGap())
388
        );
389
        layout.setVerticalGroup(
390
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
391
            .add(layout.createSequentialGroup()
392
                .add(rbRelative)
393
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
394
                .add(relativePath, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
395
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
396
                .add(rbCopy)
397
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
398
                .add(copyTo, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
399
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
400
                .add(rbAbsolute)
401
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
402
                .add(absolutePath, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
403
        );
404
    }// </editor-fold>//GEN-END:initComponents
405
    
406
    
407
    // Variables declaration - do not modify//GEN-BEGIN:variables
408
    private javax.swing.JTextField absolutePath;
409
    private javax.swing.ButtonGroup buttonGroup1;
410
    private javax.swing.JTextField copyTo;
411
    private javax.swing.JRadioButton rbAbsolute;
412
    private javax.swing.JRadioButton rbCopy;
413
    private javax.swing.JRadioButton rbRelative;
414
    private javax.swing.JTextField relativePath;
415
    // End of variables declaration//GEN-END:variables
416
417
}
(-)/dev/null (+99 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * Contributor(s):
25
 *
26
 * The Original Software is NetBeans. The Initial Developer of the Original
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
 * Microsystems, Inc. All Rights Reserved.
29
 *
30
 * If you wish your version of this file to be governed by only the CDDL
31
 * or only the GPL Version 2, indicate your decision by adding
32
 * "[Contributor] elects to include this software in this distribution
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
34
 * single choice of license, a recipient has the option to distribute
35
 * your version of this file under either the CDDL, the GPL Version 2 or
36
 * to extend the choice of license to its licensees as provided above.
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
38
 * Version 2 license, then the option applies only if the new code is
39
 * made subject to such option by the copyright holder.
40
 */
41
42
package org.netbeans.modules.project.ant;
43
44
import java.io.File;
45
import java.net.URI;
46
import org.netbeans.api.project.FileOwnerQuery;
47
import org.netbeans.api.project.Project;
48
import org.netbeans.spi.queries.CollocationQueryImplementation;
49
50
/**
51
 * A CollocationQueryImplementation implementation that collocates files based on
52
 * projects they are in.
53
 * @author Milos Kleint
54
 * @since org.netbeans.modules.project.ant/1 1.18
55
 */
56
public class FileOwnerCollocationQueryImpl implements CollocationQueryImplementation {
57
58
59
    /** Creates a new instance of FileOwnerCollocationQueryImpl */
60
    public FileOwnerCollocationQueryImpl() {
61
    }
62
63
    public File findRoot(File file) {
64
        File f = file;
65
        URI uri = f.toURI();
66
        Project prj = FileOwnerQuery.getOwner(uri);
67
        if (prj == null) {
68
            return null;
69
        }
70
        while (prj != null && f != null) {
71
            f = f.getParentFile();
72
            if (f != null) {
73
                prj = FileOwnerQuery.getOwner(f.toURI());
74
            } else {
75
                prj = null;
76
            }
77
        }
78
        return f;
79
        
80
    }
81
82
    public boolean areCollocated(File file1, File file2) {
83
        File root = findRoot (file1);
84
        boolean first = true;
85
        if (root == null) {
86
            root = findRoot (file2);
87
            first = false;
88
        }
89
        if (root != null) {
90
            String rootpath = root.getAbsolutePath() + File.separator;
91
            String check = (first ? file2.getAbsolutePath() : file1.getAbsolutePath()) + File.separator;
92
            return check.startsWith(rootpath);
93
        }
94
        return false;
95
    }
96
97
98
99
}
(-)/dev/null (+941 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.netbeans.modules.project.ant;
21
22
import java.beans.PropertyChangeEvent;
23
import java.beans.PropertyChangeListener;
24
import java.beans.PropertyChangeSupport;
25
import java.io.File;
26
import java.io.FileInputStream;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.OutputStream;
30
import java.lang.ref.Reference;
31
import java.lang.ref.WeakReference;
32
import java.net.MalformedURLException;
33
import java.net.URISyntaxException;
34
import java.net.URL;
35
import java.util.ArrayList;
36
import java.util.Arrays;
37
import java.util.Collections;
38
import java.util.HashMap;
39
import java.util.HashSet;
40
import java.util.List;
41
import java.util.Map;
42
import java.util.Properties;
43
import java.util.Set;
44
import java.util.logging.Logger;
45
import java.util.regex.Matcher;
46
import java.util.regex.Pattern;
47
import javax.swing.JFileChooser;
48
import javax.swing.event.ChangeListener;
49
import javax.swing.filechooser.FileFilter;
50
import org.netbeans.api.project.Project;
51
import org.netbeans.api.project.ProjectManager;
52
import org.netbeans.api.project.libraries.Library;
53
import org.netbeans.api.project.libraries.LibraryManager;
54
import org.netbeans.api.project.ui.OpenProjects;
55
import org.netbeans.api.queries.SharabilityQuery;
56
import org.netbeans.spi.project.AuxiliaryConfiguration;
57
import org.netbeans.spi.project.libraries.ArealLibraryProvider;
58
import org.netbeans.spi.project.libraries.LibraryImplementation;
59
import org.netbeans.spi.project.libraries.LibraryProvider;
60
import org.netbeans.spi.project.libraries.LibraryStorageArea;
61
import org.netbeans.spi.project.libraries.support.LibrariesSupport;
62
import org.netbeans.spi.project.support.ant.AntProjectEvent;
63
import org.netbeans.spi.project.support.ant.AntProjectHelper;
64
import org.netbeans.spi.project.support.ant.AntProjectListener;
65
import org.netbeans.spi.project.support.ant.EditableProperties;
66
import org.netbeans.spi.project.support.ant.PropertyProvider;
67
import org.netbeans.spi.project.support.ant.PropertyUtils;
68
import org.netbeans.spi.queries.SharabilityQueryImplementation;
69
import org.openide.filesystems.FileObject;
70
import org.openide.filesystems.FileUtil;
71
import org.openide.filesystems.URLMapper;
72
import org.openide.util.ChangeSupport;
73
import org.openide.util.Exceptions;
74
import org.openide.util.Mutex;
75
import org.openide.util.MutexException;
76
import org.openide.util.NbBundle;
77
import org.openide.util.NbCollections;
78
import org.openide.util.RequestProcessor;
79
import org.openide.util.Utilities;
80
import org.openide.util.WeakListeners;
81
import org.openide.xml.XMLUtil;
82
import org.w3c.dom.Document;
83
import org.w3c.dom.Element;
84
85
/**
86
 * Supplier of libraries declared in open projects.
87
 * @see "issue #44035"
88
 */
89
public class ProjectLibraryProvider implements ArealLibraryProvider<ProjectLibraryProvider.ProjectLibraryArea,ProjectLibraryProvider.ProjectLibraryImplementation>, PropertyChangeListener, AntProjectListener {
90
91
    private static final String NAMESPACE = "http://www.netbeans.org/ns/ant-project-libraries/1"; // NOI18N
92
    private static final String EL_LIBRARIES = "libraries"; // NOI18N
93
    private static final String EL_DEFINITIONS = "definitions"; // NOI18N
94
95
    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
96
    private AntProjectListener apl;
97
98
    public static ProjectLibraryProvider INSTANCE;
99
    /**
100
     * Default constructor for lookup.
101
     */
102
    public ProjectLibraryProvider() {
103
        INSTANCE = this;
104
    }
105
106
    public Class<ProjectLibraryArea> areaType() {
107
        return ProjectLibraryArea.class;
108
    }
109
110
    public Class<ProjectLibraryImplementation> libraryType() {
111
        return ProjectLibraryImplementation.class;
112
    }
113
114
    @Override
115
    public String toString() {
116
        return "ProjectLibraryProvider"; // NOI18N
117
    }
118
119
    // ---- management of areas ----
120
121
    public void addPropertyChangeListener(PropertyChangeListener listener) {
122
        pcs.addPropertyChangeListener(listener);
123
    }
124
125
    public void removePropertyChangeListener(PropertyChangeListener listener) {
126
        pcs.removePropertyChangeListener(listener);
127
    }
128
129
    public Set<ProjectLibraryArea> getOpenAreas() {
130
        synchronized (this) { // lazy init of OpenProjects-related stuff is better for unit testing
131
            if (apl == null) {
132
                apl = WeakListeners.create(AntProjectListener.class, this, null);
133
                OpenProjects.getDefault().addPropertyChangeListener(WeakListeners.propertyChange(this, OpenProjects.getDefault()));
134
            }
135
        }
136
        Set<ProjectLibraryArea> areas = new HashSet<ProjectLibraryArea>();
137
        for (Project p : OpenProjects.getDefault().getOpenProjects()) {
138
            AntProjectHelper helper = AntBasedProjectFactorySingleton.getHelperFor(p);
139
            if (helper == null) {
140
                // Not an Ant-based project; ignore.
141
                continue;
142
            }
143
            helper.removeAntProjectListener(apl);
144
            helper.addAntProjectListener(apl);
145
            Definitions def = findDefinitions(helper);
146
            if (def != null) {
147
                areas.add(new ProjectLibraryArea(def.mainPropertiesFile));
148
            }
149
        }
150
        return areas;
151
    }
152
153
    public ProjectLibraryArea createArea() {
154
        JFileChooser jfc = new JFileChooser();
155
        jfc.setApproveButtonText(NbBundle.getMessage(ProjectLibraryProvider.class, "ProjectLibraryProvider.open_or_create"));
156
        FileFilter filter = new FileFilter() {
157
            public boolean accept(File f) {
158
                return f.isDirectory() || (f.getName().endsWith(".properties") && !f.getName().endsWith("-private.properties")); // NOI18N
159
            }
160
            public String getDescription() {
161
                return NbBundle.getMessage(ProjectLibraryProvider.class, "ProjectLibraryProvider.properties_files");
162
            }
163
        };
164
        jfc.setFileFilter(filter);
165
        FileUtil.preventFileChooserSymlinkTraversal(jfc, null); // XXX remember last-selected dir
166
        while (jfc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
167
            File f = jfc.getSelectedFile();
168
            if (filter.accept(f)) {
169
                return new ProjectLibraryArea(f);
170
            }
171
            // Else bad filename, reopen dialog. XXX would be better to just disable OK button, but not sure how...?
172
        }
173
        return null;
174
    }
175
176
    public ProjectLibraryArea loadArea(URL location) {
177
        if (location.getProtocol().equals("file") && location.getPath().endsWith(".properties")) { // NOI18N
178
            try {
179
                return new ProjectLibraryArea(new File(location.toURI()));
180
            } catch (URISyntaxException x) {
181
                Exceptions.printStackTrace(x);
182
            }
183
        }
184
        return null;
185
    }
186
187
    public void propertyChange(PropertyChangeEvent ev) {
188
        if (OpenProjects.PROPERTY_OPEN_PROJECTS.equals(ev.getPropertyName())) {
189
            pcs.firePropertyChange(ArealLibraryProvider.PROP_OPEN_AREAS, null, null);
190
        }
191
    }
192
193
    public void configurationXmlChanged(AntProjectEvent ev) {
194
        pcs.firePropertyChange(ArealLibraryProvider.PROP_OPEN_AREAS, null, null);
195
    }
196
197
    public void propertiesChanged(AntProjectEvent ev) {}
198
199
    // ---- management of libraries ----
200
201
    private boolean listening = true;
202
    private final Map<ProjectLibraryArea,Reference<LP>> providers = new HashMap<ProjectLibraryArea,Reference<LP>>();
203
204
    private final class LP implements LibraryProvider<ProjectLibraryImplementation>, FileChangeSupportListener {
205
206
        private final ProjectLibraryArea area;
207
        private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
208
        private final Map<String,ProjectLibraryImplementation> libraries;
209
210
        LP(ProjectLibraryArea area) {
211
            this.area = area;
212
            libraries = calculate(area);
213
            Definitions defs = new Definitions(area.mainPropertiesFile);
214
            FileChangeSupport.DEFAULT.addListener(this, defs.mainPropertiesFile);
215
            FileChangeSupport.DEFAULT.addListener(this, defs.privatePropertiesFile);
216
        }
217
218
        public synchronized ProjectLibraryImplementation[] getLibraries() {
219
            return libraries.values().toArray(new ProjectLibraryImplementation[libraries.size()]);
220
        }
221
222
        ProjectLibraryImplementation getLibrary(String name) {
223
            return libraries.get(name);
224
        }
225
226
        public void addPropertyChangeListener(PropertyChangeListener listener) {
227
            pcs.addPropertyChangeListener(listener);
228
        }
229
230
        public void removePropertyChangeListener(PropertyChangeListener listener) {
231
            pcs.removePropertyChangeListener(listener);
232
        }
233
234
        public void fileCreated(FileChangeSupportEvent event) {
235
            recalculate();
236
        }
237
238
        public void fileDeleted(FileChangeSupportEvent event) {
239
            recalculate();
240
        }
241
242
        public void fileModified(FileChangeSupportEvent event) {
243
            recalculate();
244
        }
245
246
        private synchronized void recalculate() {
247
            if (delta(libraries, calculate(area))) {
248
                pcs.firePropertyChange(LibraryProvider.PROP_LIBRARIES, null, null);
249
            }
250
        }
251
252
    }
253
254
    public synchronized LP getLibraries(ProjectLibraryArea area) {
255
        Reference<LP> rlp = providers.get(area);
256
        LP lp = rlp != null ? rlp.get() : null;
257
        if (lp == null) {
258
            lp = new LP(area);
259
            providers.put(area, new WeakReference<LP>(lp));
260
        }
261
        return lp;
262
    }
263
264
    public ProjectLibraryImplementation createLibrary(String type, String name, ProjectLibraryArea area, Map<String,List<URL>> contents) throws IOException {
265
        File f = area.mainPropertiesFile;
266
        assert listening;
267
        listening = false;
268
        try {
269
            if (type.equals("j2se")) { // NOI18N
270
                replaceProperty(f, true, "libs." + name + ".classpath", ""); // NOI18N
271
            } else {
272
                replaceProperty(f, false, "libs." + name + ".type", type); // NOI18N
273
            }
274
        } finally {
275
            listening = true;
276
        }
277
        LP lp = getLibraries(area);
278
        lp.recalculate();
279
        ProjectLibraryImplementation impl = lp.getLibrary(name);
280
        assert impl != null : name + " not found in " + f;
281
        for (Map.Entry<String,List<URL>> entry : contents.entrySet()) {
282
            impl.setContent(entry.getKey(), entry.getValue());
283
        }
284
        return impl;
285
    }
286
287
    public void remove(ProjectLibraryImplementation pli) throws IOException {
288
        String prefix = "libs." + pli.name + "."; // NOI18N
289
        // XXX run atomically to fire changes just once:
290
        for (File f : new File[] {pli.mainPropertiesFile, pli.privatePropertiesFile}) {
291
            for (String k : loadProperties(f).keySet()) {
292
                if (k.startsWith(prefix)) {
293
                    replaceProperty(f, false, k);
294
                }
295
            }
296
        }
297
    }
298
299
    /** one definitions entry */
300
    private static final class Definitions {
301
        /** may or may not exist; in case you need to listen to it */
302
        final File mainPropertiesFile;
303
        /** similar to {@link #mainPropertiesFile} but for *-private.properties; null if main is not *.properties */
304
        final File privatePropertiesFile;
305
        private Map<String,String> properties;
306
        Definitions(File mainPropertiesFile) {
307
            this.mainPropertiesFile = mainPropertiesFile;
308
            String suffix = ".properties"; // NOI18N
309
            String name = mainPropertiesFile.getName();
310
            if (name.endsWith(suffix)) {
311
                privatePropertiesFile = new File(mainPropertiesFile.getParentFile(), name.substring(0, name.length() - suffix.length()) + "-private" + suffix); // NOI18N
312
            } else {
313
                privatePropertiesFile = null;
314
            }
315
        }
316
        /** with ${base} resolved according to resolveBase; may be empty or have junk defs */
317
        synchronized Map<String,String> properties(boolean resolveBase) {
318
            if (properties == null) {
319
                properties = new HashMap<String,String>();
320
                String basedir = mainPropertiesFile.getParent();
321
                for (Map.Entry<String,String> entry : loadProperties(mainPropertiesFile).entrySet()) {
322
                    String value = entry.getValue();
323
                    if (resolveBase) {
324
                        value = value.replace("${base}", basedir); // NOI18N
325
                    }
326
                    properties.put(entry.getKey(), value.replace('/', File.separatorChar));
327
                }
328
                if (privatePropertiesFile != null) {
329
                    for (Map.Entry<String,String> entry : loadProperties(privatePropertiesFile).entrySet()) {
330
                        String value = entry.getValue();
331
                        if (resolveBase) {
332
                            value = value.replace("${base}", basedir); // NOI18N
333
                        }
334
                        properties.put(entry.getKey(), value.replace('/', File.separatorChar));
335
                    }
336
                }
337
            }
338
            return properties;
339
        }
340
    }
341
342
    private static Definitions findDefinitions(AntProjectHelper helper) {
343
        String text = getLibrariesLocationText(helper.createAuxiliaryConfiguration());
344
        if (text != null) {
345
            File mainPropertiesFile = helper.resolveFile(text);
346
            if (mainPropertiesFile.getName().endsWith(".properties")) { // NOI18N
347
                return new Definitions(mainPropertiesFile);
348
            }
349
        }
350
        return null;
351
    }
352
353
    public static File getLibrariesLocation(AuxiliaryConfiguration aux, File projectFolder) {
354
        String text = getLibrariesLocationText(aux);
355
        if (text != null) {
356
            return PropertyUtils.resolveFile(projectFolder, text);
357
        }
358
        return null;
359
    }
360
    
361
    /**
362
     * Returns libraries location as text.
363
     */
364
    public static String getLibrariesLocationText(AuxiliaryConfiguration aux) {
365
        Element libraries = aux.getConfigurationFragment(EL_LIBRARIES, NAMESPACE, true);
366
        if (libraries != null) {
367
            for (Element definitions : Util.findSubElements(libraries)) {
368
                assert definitions.getLocalName().equals(EL_DEFINITIONS) : definitions;
369
                String text = Util.findText(definitions);
370
                assert text != null : aux;
371
                return text;
372
            }
373
        }
374
        return null;
375
    }
376
    
377
    private static Map<String,String> loadProperties(File f) {
378
        if (!f.isFile()) {
379
            return Collections.emptyMap();
380
        }
381
        Properties p = new Properties();
382
        try {
383
            InputStream is = new FileInputStream(f);
384
            try {
385
                p.load(is);
386
            } finally {
387
                is.close();
388
            }
389
            return NbCollections.checkedMapByFilter(p, String.class, String.class, true);
390
        } catch (IOException x) {
391
            Exceptions.attachMessage(x, "Loading: " + f); // NOI18N
392
            Exceptions.printStackTrace(x);
393
            return Collections.emptyMap();
394
        }
395
    }
396
397
    private static final Pattern LIBS_LINE = Pattern.compile("libs\\.([^.]+)\\.([^.]+)"); // NOI18N
398
    private static Map<String,ProjectLibraryImplementation> calculate(ProjectLibraryArea area) {
399
        Map<String,ProjectLibraryImplementation> libs = new HashMap<String,ProjectLibraryImplementation>();
400
        Definitions def = new Definitions(area.mainPropertiesFile);
401
        Map<String,Map<String,String>> data = new HashMap<String,Map<String,String>>();
402
        for (Map.Entry<String,String> entry : def.properties(false).entrySet()) {
403
            Matcher match = LIBS_LINE.matcher(entry.getKey());
404
            if (!match.matches()) {
405
                continue;
406
            }
407
            String name = match.group(1);
408
            Map<String,String> subdata = data.get(name);
409
            if (subdata == null) {
410
                subdata = new HashMap<String,String>();
411
                data.put(name, subdata);
412
            }
413
            subdata.put(match.group(2), entry.getValue());
414
        }
415
        for (Map.Entry<String,Map<String,String>> entry : data.entrySet()) {
416
            String name = entry.getKey();
417
            String type = "j2se"; // NOI18N
418
            String description = null;
419
            Map<String,List<URL>> contents = new HashMap<String,List<URL>>();
420
            for (Map.Entry<String,String> subentry : entry.getValue().entrySet()) {
421
                String k = subentry.getKey();
422
                if (k.equals("type")) { // NOI18N
423
                    type = subentry.getValue();
424
                } else if (k.equals("name")) { // NOI18N
425
                    // XXX currently overriding display name is not supported
426
                } else if (k.equals("description")) { // NOI18N
427
                    description = subentry.getValue();
428
                } else {
429
                    String[] path = PropertyUtils.tokenizePath(subentry.getValue());
430
                    List<URL> volume = new ArrayList<URL>(path.length);
431
                    for (String component : path) {
432
                        File f = new File(component.replace('/', File.separatorChar).replace('\\', File.separatorChar).replace("${base}"+File.separatorChar, ""));
433
                        File normalizedFile = FileUtil.normalizeFile(new File(component.replace('/', File.separatorChar).replace('\\', File.separatorChar).replace("${base}", area.mainPropertiesFile.getParent())));
434
                        try {
435
                            URL u = LibrariesSupport.convertFileToURL(f);
436
                            if (FileUtil.isArchiveFile(normalizedFile.toURI().toURL())) {
437
                                u = FileUtil.getArchiveRoot(u);
438
                            } else if (!u.toExternalForm().endsWith("/")) {
439
                                u = new URL(u.toExternalForm() + "/");
440
                            }
441
                            volume.add(u);
442
                        } catch (MalformedURLException x) {
443
                            Exceptions.printStackTrace(x);
444
                        }
445
                    }
446
                    contents.put(k, volume);
447
                }
448
            }
449
            libs.put(name, new ProjectLibraryImplementation(def.mainPropertiesFile, def.privatePropertiesFile, type, name, description, contents));
450
        }
451
        return libs;
452
    }
453
454
    private synchronized boolean delta(Map<String,ProjectLibraryImplementation> libraries, Map<String,ProjectLibraryImplementation> newLibraries) {
455
        if (!listening) {
456
            return false;
457
        }
458
        Set<String> added = new HashSet<String>(newLibraries.keySet());
459
        added.removeAll(libraries.keySet());
460
        Set<String> removed = new HashSet<String>();
461
        for (Map.Entry<String,ProjectLibraryImplementation> entry : libraries.entrySet()) {
462
            String name = entry.getKey();
463
            ProjectLibraryImplementation old = entry.getValue();
464
            ProjectLibraryImplementation nue = newLibraries.get(name);
465
            if (nue == null) {
466
                removed.add(name);
467
                continue;
468
            }
469
            if (!old.type.equals(nue.type)) {
470
                // Cannot fire this.
471
                added.add(name);
472
                removed.add(name);
473
                libraries.put(name, nue);
474
                continue;
475
            }
476
            assert old.name.equals(nue.name);
477
            if (!Utilities.compareObjects(old.description, nue.description)) {
478
                old.description = nue.description;
479
                old.pcs.firePropertyChange(LibraryImplementation.PROP_DESCRIPTION, null, null);
480
            }
481
            if (!old.contents.equals(nue.contents)) {
482
                old.contents = nue.contents;
483
                old.pcs.firePropertyChange(LibraryImplementation.PROP_CONTENT, null, null);
484
            }
485
        }
486
        for (String name : added) {
487
            libraries.put(name, newLibraries.get(name));
488
        }
489
        for (String name : removed) {
490
            libraries.remove(name);
491
        }
492
        return !added.isEmpty() || !removed.isEmpty();
493
    }
494
495
    static final class ProjectLibraryImplementation implements LibraryImplementation {
496
497
        final File mainPropertiesFile, privatePropertiesFile;
498
        final String type;
499
        String name;
500
        String description;
501
        Map<String,List<URL>> contents;
502
        final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
503
504
        ProjectLibraryImplementation(File mainPropertiesFile, File privatePropertiesFile, String type, String name, String description, Map<String,List<URL>> contents) {
505
            this.mainPropertiesFile = mainPropertiesFile;
506
            this.privatePropertiesFile = privatePropertiesFile;
507
            this.type = type;
508
            this.name = name;
509
            this.description = description;
510
            this.contents = contents;
511
        }
512
513
        public String getType() {
514
            return type;
515
        }
516
517
        public String getName() {
518
            return name;
519
        }
520
521
        public String getDescription() {
522
            return description;
523
        }
524
525
        public String getLocalizingBundle() {
526
            return null;
527
        }
528
529
        public List<URL> getContent(String volumeType) throws IllegalArgumentException {
530
            List<URL> content = contents.get(volumeType);
531
            if (content == null) {
532
                content = Collections.emptyList();
533
            }
534
            return content;
535
        }
536
537
        public void setName(String name) {
538
            this.name = name;
539
            pcs.firePropertyChange(LibraryImplementation.PROP_NAME, null, null);
540
            throw new UnsupportedOperationException(); // XXX will anyone call this?
541
        }
542
543
        public void setDescription(String text) {
544
            throw new UnsupportedOperationException(); // XXX will anyone call this?
545
        }
546
547
        public void setContent(String volumeType, List<URL> path) throws IllegalArgumentException {
548
            if (path.equals(getContent(volumeType))) {
549
                return;
550
            }
551
            List<String> value = new ArrayList<String>();
552
            for (URL entry : path) {
553
                if ("jar".equals(entry.getProtocol())) { // NOI18N
554
                    if (!entry.toExternalForm().endsWith("!/")) { // NOI18N
555
                        throw new IllegalArgumentException("Non-root folders in JARs not permitted"); // NOI18N
556
                    }
557
                    entry = FileUtil.getArchiveFile(entry);
558
                } else if (!"file".equals(entry.getProtocol())) { // NOI18N
559
                    throw new IllegalArgumentException(entry.toExternalForm());
560
                }
561
                File f = LibrariesSupport.convertURLToFile(entry);
562
                // store properties always separated by '/' for consistency
563
                String s;
564
                if (f.isAbsolute()) {
565
                    s = f.getAbsolutePath().replace('\\', '/');
566
                } else {
567
                    s = "${base}/" + f.getPath().replace('\\', '/'); // NOI18N
568
                }
569
                if (value.size()+1 != path.size()) {
570
                    s += File.pathSeparatorChar;
571
                }
572
                value.add(s);
573
            }
574
            String key = "libs." + name + "." + volumeType; // NOI18N
575
            try {
576
                replaceProperty(mainPropertiesFile, true, key, value.toArray(new String[value.size()]));
577
            } catch (IOException x) {
578
                throw new IllegalArgumentException(x);
579
            }
580
        }
581
582
        public void setLocalizingBundle(String resourceName) {
583
            throw new UnsupportedOperationException();
584
        }
585
586
        public void addPropertyChangeListener(PropertyChangeListener l) {
587
            pcs.addPropertyChangeListener(l);
588
        }
589
590
        public void removePropertyChangeListener(PropertyChangeListener l) {
591
            pcs.removePropertyChangeListener(l);
592
        }
593
594
        @Override
595
        public String toString() {
596
            return "ProjectLibraryImplementation[name=" + name + ",file=" + mainPropertiesFile + ",contents=" + contents + "]"; // NOI18N
597
        }
598
599
    }
600
601
    private static void replaceProperty(File propfile, boolean classPathLikeValue, String key, String... value) throws IOException {
602
        EditableProperties ep = new EditableProperties();
603
        if (propfile.isFile()) {
604
            InputStream is = new FileInputStream(propfile);
605
            try {
606
                ep.load(is);
607
            } finally {
608
                is.close();
609
            }
610
        }
611
        if (Utilities.compareObjects(value, ep.getProperty(key))) {
612
            return;
613
        }
614
        if (value.length > 0) {
615
            if (classPathLikeValue) {
616
                ep.setProperty(key, value);
617
            } else {
618
                assert value.length == 1 : Arrays.asList(value);
619
                ep.setProperty(key, value[0]);
620
            }
621
        } else {
622
            ep.remove(key);
623
        }
624
        FileObject fo = FileUtil.createData(propfile);
625
        OutputStream os = fo.getOutputStream();
626
        try {
627
            ep.store(os);
628
        } finally {
629
            os.close();
630
        }
631
    }
632
633
    static final class ProjectLibraryArea implements LibraryStorageArea {
634
635
        final File mainPropertiesFile;
636
637
        ProjectLibraryArea(File mainPropertiesFile) {
638
            assert mainPropertiesFile.getName().endsWith(".properties") : mainPropertiesFile;
639
            this.mainPropertiesFile = mainPropertiesFile;
640
        }
641
642
        public String getDisplayName() {
643
            return mainPropertiesFile.getAbsolutePath();
644
        }
645
646
        public URL getLocation() {
647
            try {
648
                return mainPropertiesFile.toURI().toURL();
649
            } catch (MalformedURLException x) {
650
                throw new AssertionError(x);
651
            }
652
        }
653
654
        public boolean equals(Object obj) {
655
            return obj instanceof ProjectLibraryArea && ((ProjectLibraryArea) obj).mainPropertiesFile.equals(mainPropertiesFile);
656
        }
657
658
        public int hashCode() {
659
            return mainPropertiesFile.hashCode();
660
        }
661
662
        @Override
663
        public String toString() {
664
            return "ProjectLibraryArea[" + mainPropertiesFile + "]"; // NOI18N
665
        }
666
667
    }
668
669
    /**
670
     * Used from {@link AntProjectHelper#getProjectLibrariesPropertyProvider}.
671
     * @param helper a project
672
     * @return a provider of project library definition properties
673
     */
674
    public static PropertyProvider createPropertyProvider(final AntProjectHelper helper) {
675
        class PP implements PropertyProvider, FileChangeSupportListener, AntProjectListener {
676
            final ChangeSupport cs = new ChangeSupport(this);
677
            final Set<File> listeningTo = new HashSet<File>();
678
            {
679
                helper.addAntProjectListener(WeakListeners.create(AntProjectListener.class, this, helper));
680
            }
681
            private void listenTo(File f, Set<File> noLongerListeningTo) {
682
                if (f != null) {
683
                    noLongerListeningTo.remove(f);
684
                    if (listeningTo.add(f)) {
685
                        FileChangeSupport.DEFAULT.addListener(this, f);
686
                    }
687
                }
688
            }
689
            public synchronized Map<String,String> getProperties() {
690
                Map<String,String> m = new HashMap<String,String>();
691
                // XXX add an AntProjectListener
692
                Set<File> noLongerListeningTo = new HashSet<File>(listeningTo);
693
                Definitions def = findDefinitions(helper);
694
                if (def != null) {
695
                    m.putAll(def.properties(true));
696
                    listenTo(def.mainPropertiesFile, noLongerListeningTo);
697
                    listenTo(def.privatePropertiesFile, noLongerListeningTo);
698
                }
699
                for (File f : noLongerListeningTo) {
700
                    listeningTo.remove(f);
701
                    FileChangeSupport.DEFAULT.removeListener(this, f);
702
                }
703
                return m;
704
            }
705
            public void addChangeListener(ChangeListener l) {
706
                cs.addChangeListener(l);
707
            }
708
            public void removeChangeListener(ChangeListener l) {
709
                cs.removeChangeListener(l);
710
            }
711
            public void fileCreated(FileChangeSupportEvent event) {
712
                fireChangeNowOrLater();
713
            }
714
            public void fileDeleted(FileChangeSupportEvent event) {
715
                fireChangeNowOrLater();
716
            }
717
            public void fileModified(FileChangeSupportEvent event) {
718
                fireChangeNowOrLater();
719
            }
720
            void fireChangeNowOrLater() {
721
                // See PropertyUtils.FilePropertyProvider.
722
                if (!cs.hasListeners()) {
723
                    return;
724
                }
725
                final Mutex.Action<Void> action = new Mutex.Action<Void>() {
726
                    public Void run() {
727
                        cs.fireChange();
728
                        return null;
729
                    }
730
                };
731
                if (ProjectManager.mutex().isWriteAccess() || FIRE_CHANGES_SYNCH) {
732
                    ProjectManager.mutex().readAccess(action);
733
                } else if (ProjectManager.mutex().isReadAccess()) {
734
                    action.run();
735
                } else {
736
                    RP.post(new Runnable() {
737
                        public void run() {
738
                            ProjectManager.mutex().readAccess(action);
739
                        }
740
                    });
741
                }
742
            }
743
            public void configurationXmlChanged(AntProjectEvent ev) {
744
                cs.fireChange();
745
            }
746
            public void propertiesChanged(AntProjectEvent ev) {}
747
        }
748
        return new PP();
749
    }
750
    private static final RequestProcessor RP = new RequestProcessor("ProjectLibraryProvider.RP"); // NOI18N
751
    public static boolean FIRE_CHANGES_SYNCH = false; // used by tests
752
753
    /**
754
     * Is this library reachable from this project? Returns true if given library
755
     * is defined in libraries location associated with this project.
756
     */
757
    public static boolean isReachableLibrary(Library library, AntProjectHelper helper) {
758
        URL location = library.getManager().getLocation();
759
        if (location == null) {
760
            return false;
761
        }
762
        ProjectLibraryArea area = INSTANCE.loadArea(location);
763
        if (area == null) {
764
            return false;
765
        }
766
        ProjectLibraryImplementation pli = INSTANCE.getLibraries(area).getLibrary(library.getName());
767
        if (pli == null) {
768
            return false;
769
        }
770
        Definitions def = findDefinitions(helper);
771
        if (def == null) {
772
            return false;
773
        }
774
        return def.mainPropertiesFile.equals(pli.mainPropertiesFile);
775
    }
776
    
777
    /**
778
     * Create element for shared libraries to store in project.xml.
779
     * 
780
     * @param doc XML document
781
     * @param location project relative or absolute OS path; cannot be null
782
     * @return element
783
     */
784
    public static Element createLibrariesElement(Document doc, String location) {
785
        Element libraries = doc.createElementNS(NAMESPACE, EL_LIBRARIES);
786
        libraries.appendChild(libraries.getOwnerDocument().createElementNS(NAMESPACE, EL_DEFINITIONS)).
787
            appendChild(libraries.getOwnerDocument().createTextNode(location));
788
        return libraries;
789
    }
790
791
    /**
792
     * Used from {@link ReferenceHelper#getProjectLibraryManager}.
793
     */
794
    public static LibraryManager getProjectLibraryManager(AntProjectHelper helper) {
795
        Definitions defs = findDefinitions(helper);
796
        if (defs != null) {
797
            try {
798
                return LibraryManager.forLocation(defs.mainPropertiesFile.toURI().toURL());
799
            } catch (MalformedURLException x) {
800
                Exceptions.printStackTrace(x);
801
            }
802
        }
803
        return null;
804
    }
805
806
    /**
807
     * Stores given libraries location in given project.
808
     */
809
    public static void setLibrariesLocation(AntProjectHelper helper, String librariesDefinition) {
810
        Element libraries = helper.createAuxiliaryConfiguration().getConfigurationFragment(EL_LIBRARIES, NAMESPACE, true);
811
        if (libraries == null) {
812
            libraries = XMLUtil.createDocument("dummy", null, null, null).createElementNS(NAMESPACE, EL_LIBRARIES); // NOI18N
813
        } else {
814
            List<Element> elements = Util.findSubElements(libraries);
815
            if (elements.size() == 1) {
816
                libraries.removeChild(elements.get(0));
817
            }
818
        }
819
        libraries.appendChild(libraries.getOwnerDocument().createElementNS(NAMESPACE, EL_DEFINITIONS)).
820
            appendChild(libraries.getOwnerDocument().createTextNode(librariesDefinition));
821
        helper.createAuxiliaryConfiguration().putConfigurationFragment(libraries, true);
822
    }
823
824
    /**
825
     * Used from {@link org.netbeans.spi.project.support.ant.SharabilityQueryImpl}.
826
     */
827
    public static List<String> getUnsharablePathsWithinProject(AntProjectHelper helper) {
828
        List<String> paths = new ArrayList<String>();
829
        Definitions defs = findDefinitions(helper);
830
        if (defs != null) {
831
            if (defs.privatePropertiesFile != null) {
832
                paths.add(defs.privatePropertiesFile.getAbsolutePath());
833
            }
834
        }
835
        return paths;
836
    }
837
838
    public static final class SharabilityQueryImpl implements SharabilityQueryImplementation {
839
840
        /** Default constructor for lookup. */
841
        public SharabilityQueryImpl() {}
842
843
        public int getSharability(File file) {
844
            if (file.getName().endsWith("-private.properties")) { // NOI18N
845
                return SharabilityQuery.NOT_SHARABLE;
846
            } else {
847
                return SharabilityQuery.UNKNOWN;
848
            }
849
        }
850
851
    }
852
853
    /**
854
     * Used from {@link org.netbeans.spi.project.support.ant.ReferenceHelper}.
855
     */
856
    public static Library copyLibrary(final Library lib, final URL location, 
857
            final boolean generateLibraryUniqueName) throws IOException {
858
        final File libBaseFolder = LibrariesSupport.convertURLToFile(location).getParentFile();
859
        FileObject sharedLibFolder;
860
        try {
861
            sharedLibFolder = ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction<FileObject>() {
862
                public FileObject run() throws IOException {
863
                    FileObject lf = FileUtil.toFileObject(libBaseFolder);
864
                    return lf.createFolder(getUniqueName(lf, lib.getName(), null));
865
                }
866
            });
867
        } catch (MutexException ex) {
868
            throw (IOException)ex.getException();
869
        }
870
        final Map<String, List<URL>> content = new HashMap<String, List<URL>>();
871
        String[] volumes = LibrariesSupport.getLibraryTypeProvider(lib.getType()).getSupportedVolumeTypes();
872
        for (String volume : volumes) {
873
            List<URL> volumeContent = new ArrayList<URL>();
874
            for (URL libEntry : lib.getContent(volume)) {
875
                if ("jar".equals(libEntry.getProtocol())) { // NOI18N
876
                    libEntry = FileUtil.getArchiveFile(libEntry);
877
                }
878
                FileObject libEntryFO = URLMapper.findFileObject(libEntry);
879
                if (libEntryFO == null) {
880
                    if (!"file".equals(libEntry.getProtocol()) && // NOI18N
881
                        !"nbinst".equals(libEntry.getProtocol())) { // NOI18N
882
                        Logger.getLogger(ProjectLibraryProvider.class.getName()).warning("copyLibrary is ignoring entry "+libEntry);
883
                        continue;
884
                    } else {
885
                        Logger.getLogger(ProjectLibraryProvider.class.getName()).warning("Library '"+lib.getDisplayName()+ // NOI18N
886
                            "' contains entry ("+libEntry+") which does not exist. This entry is ignored and will not be copied to sharable libraries location."); // NOI18N
887
                        continue;
888
                    }
889
                }
890
                FileObject newFO;
891
                String name;
892
                if (libEntryFO.isFolder()) {
893
                    newFO = FileChooserAccessory.copyFolderRecursively(libEntryFO, sharedLibFolder);
894
                    name = sharedLibFolder.getName()+File.separatorChar+newFO.getName()+File.separatorChar;
895
                } else {
896
                    String libEntryName = getUniqueName(sharedLibFolder, libEntryFO.getName(), libEntryFO.getExt());
897
                    newFO = FileUtil.copyFile(libEntryFO, sharedLibFolder, libEntryName);
898
                    name = sharedLibFolder.getName()+File.separatorChar+newFO.getNameExt();
899
                }
900
                volumeContent.add(LibrariesSupport.convertFileToURL(new File(name)));
901
            }
902
            content.put(volume, volumeContent);
903
        }
904
        final LibraryManager man = LibraryManager.forLocation(location);
905
        try {
906
            return ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction<Library>() {
907
                public Library run() throws IOException {
908
                    String name = lib.getName();
909
                    if (generateLibraryUniqueName) {
910
                        int index = 2;
911
                        while (man.getLibrary(name) != null) {
912
                            name = lib.getName() + "-" + index;
913
                            index++;
914
                        }
915
                    }
916
                    return man.createLibrary(lib.getType(), name, content);
917
                }
918
            });
919
        } catch (MutexException ex) {
920
            throw (IOException)ex.getException();
921
        }
922
    }
923
924
    /**
925
     * Generate unique file name for the given folder, base name and optionally extension.
926
     * @param baseFolder folder to generate new file name in
927
     * @param nameFileName file name without extension
928
     * @param extension can be null for folder
929
     * @return new file name without extension
930
     */
931
    private static String getUniqueName(FileObject baseFolder, String nameFileName, String extension) {
932
        int suffix = 2;
933
        String name = nameFileName;  //NOI18N
934
        while (baseFolder.getFileObject(name + (extension != null ? "." + extension : "")) != null) {
935
            name = nameFileName + "-" + suffix; // NOI18N
936
            suffix++;
937
        }
938
        return name;
939
    }
940
    
941
}
(-)/dev/null (+66 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8" ?>
2
3
<Form version="1.5" maxVersion="1.5" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
4
  <AuxValues>
5
    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
6
    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
7
    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
8
    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
9
    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="2"/>
10
    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
11
    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
12
    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
13
  </AuxValues>
14
15
  <Layout>
16
    <DimensionLayout dim="0">
17
      <Group type="103" groupAlignment="0" attributes="0">
18
          <Group type="102" alignment="0" attributes="0">
19
              <EmptySpace max="-2" attributes="0"/>
20
              <Group type="103" groupAlignment="0" attributes="0">
21
                  <Component id="jPanel1" alignment="0" max="32767" attributes="0"/>
22
                  <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
23
              </Group>
24
              <EmptySpace max="-2" attributes="0"/>
25
          </Group>
26
      </Group>
27
    </DimensionLayout>
28
    <DimensionLayout dim="1">
29
      <Group type="103" groupAlignment="0" attributes="0">
30
          <Group type="102" alignment="0" attributes="0">
31
              <EmptySpace max="-2" attributes="0"/>
32
              <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
33
              <EmptySpace max="-2" attributes="0"/>
34
              <Component id="jPanel1" max="32767" attributes="0"/>
35
              <EmptySpace max="-2" attributes="0"/>
36
          </Group>
37
      </Group>
38
    </DimensionLayout>
39
  </Layout>
40
  <SubComponents>
41
    <Component class="javax.swing.JLabel" name="jLabel1">
42
      <Properties>
43
        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
44
          <ResourceString bundle="org/netbeans/modules/project/ant/Bundle.properties" key="RelativizeFilePathCustomizer.jLabel1.text" replaceFormat="java.text.MessageFormat.format(org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;), {arguments})">
45
            <Argument index="0" javacode="fileToRelativize.getName()"/>
46
          </ResourceString>
47
        </Property>
48
      </Properties>
49
    </Component>
50
    <Container class="javax.swing.JPanel" name="jPanel1">
51
52
      <Layout>
53
        <DimensionLayout dim="0">
54
          <Group type="103" groupAlignment="0" attributes="0">
55
              <EmptySpace min="0" pref="380" max="32767" attributes="0"/>
56
          </Group>
57
        </DimensionLayout>
58
        <DimensionLayout dim="1">
59
          <Group type="103" groupAlignment="0" attributes="0">
60
              <EmptySpace min="0" pref="258" max="32767" attributes="0"/>
61
          </Group>
62
        </DimensionLayout>
63
      </Layout>
64
    </Container>
65
  </SubComponents>
66
</Form>
(-)/dev/null (+133 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * Contributor(s):
25
 *
26
 * The Original Software is NetBeans. The Initial Developer of the Original
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
 * Microsystems, Inc. All Rights Reserved.
29
 *
30
 * If you wish your version of this file to be governed by only the CDDL
31
 * or only the GPL Version 2, indicate your decision by adding
32
 * "[Contributor] elects to include this software in this distribution
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
34
 * single choice of license, a recipient has the option to distribute
35
 * your version of this file under either the CDDL, the GPL Version 2 or
36
 * to extend the choice of license to its licensees as provided above.
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
38
 * Version 2 license, then the option applies only if the new code is
39
 * made subject to such option by the copyright holder.
40
 */
41
42
package org.netbeans.modules.project.ant;
43
44
import java.awt.BorderLayout;
45
import java.io.File;
46
import java.io.IOException;
47
48
/**
49
 * Wrapper around FileChooserAccessory to let user decide how to reference file.
50
 */
51
public class RelativizeFilePathCustomizer extends javax.swing.JPanel {
52
    
53
    private File fileToRelativize;
54
    private File baseFolder;
55
    private File sharedLibrariesFolder;
56
    private boolean copyAllowed;
57
    private FileChooserAccessory fileChooserAccessory;
58
    
59
    /** Creates new form RelativizeFilePathCustomizer */
60
    public RelativizeFilePathCustomizer(File fileToRelativize, File baseFolder, File sharedLibrariesFolder, boolean copyAllowed) {
61
        this.fileToRelativize = fileToRelativize;
62
        this.baseFolder = baseFolder;
63
        this.sharedLibrariesFolder = sharedLibrariesFolder;
64
        this.copyAllowed = copyAllowed;
65
        initComponents();
66
        fileChooserAccessory = new FileChooserAccessory(baseFolder, sharedLibrariesFolder, copyAllowed, fileToRelativize);
67
        jPanel1.setLayout(new BorderLayout());
68
        jPanel1.add(fileChooserAccessory);
69
    }
70
    
71
    public File getFile() throws IOException {
72
        fileChooserAccessory.copyFilesIfNecessary();
73
        if (fileChooserAccessory.isRelative()) {
74
            return fileChooserAccessory.getFiles()[0];
75
        } else {
76
            return fileToRelativize;
77
        }
78
    }
79
            
80
    
81
    /** This method is called from within the constructor to
82
     * initialize the form.
83
     * WARNING: Do NOT modify this code. The content of this method is
84
     * always regenerated by the Form Editor.
85
     */
86
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
87
    private void initComponents() {
88
89
        jLabel1 = new javax.swing.JLabel();
90
        jPanel1 = new javax.swing.JPanel();
91
92
        jLabel1.setText(java.text.MessageFormat.format(org.openide.util.NbBundle.getMessage(RelativizeFilePathCustomizer.class, "RelativizeFilePathCustomizer.jLabel1.text"), new Object[] {fileToRelativize.getName()})); // NOI18N
93
94
        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
95
        jPanel1.setLayout(jPanel1Layout);
96
        jPanel1Layout.setHorizontalGroup(
97
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
98
            .add(0, 380, Short.MAX_VALUE)
99
        );
100
        jPanel1Layout.setVerticalGroup(
101
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
102
            .add(0, 258, Short.MAX_VALUE)
103
        );
104
105
        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
106
        this.setLayout(layout);
107
        layout.setHorizontalGroup(
108
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
109
            .add(layout.createSequentialGroup()
110
                .addContainerGap()
111
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
112
                    .add(jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
113
                    .add(jLabel1))
114
                .addContainerGap())
115
        );
116
        layout.setVerticalGroup(
117
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
118
            .add(layout.createSequentialGroup()
119
                .addContainerGap()
120
                .add(jLabel1)
121
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
122
                .add(jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
123
                .addContainerGap())
124
        );
125
    }// </editor-fold>//GEN-END:initComponents
126
    
127
    
128
    // Variables declaration - do not modify//GEN-BEGIN:variables
129
    private javax.swing.JLabel jLabel1;
130
    private javax.swing.JPanel jPanel1;
131
    // End of variables declaration//GEN-END:variables
132
    
133
}
(-)/dev/null (+32 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
Schema for references to versionable library descriptors. See #44035.
4
Since: org.netbeans.modules.project.ant/1 1.18
5
Include as a fragment in an nbproject/project.xml file.
6
Content of a <definitions> element is a relative path to a properties file.
7
('/'- or '\'-separated but NOT in URI syntax, e.g. do not encode spaces as %20.)
8
The path is resolved against the project's base directory.
9
The property values that are recognized are of the form libs.NAME.KEY
10
where NAME is the (internal) name of a library,
11
and KEY is one of the following (refer to http://www.netbeans.org/dtds/library-declaration-1_0.dtd for comparisons):
12
1. 'type' - e.g. 'j2me', as in <library>/<type>; default is 'j2se'
13
2. 'name' - e.g. 'My Library', as in <name> (localizing bundles not supported); default is internal name
14
3. 'description' - e.g. 'Some library of mine.', as in <description>; default is null
15
4. 'classpath', 'src', 'javadoc', etc. - according to the <volume>/<type>, as in $userdir/build.properties
16
Volume-typed values should be paths (separated with ':' or ';') of file or folder names (usually relative).
17
The special token '${base}' may be used to refer to the directory in which the properties file resides.
18
For a definitions file PATH.properties, there may also be a file PATH-private.properties
19
which can override or supplement its definitions (typically with absolute filenames).
20
-->
21
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
22
            targetNamespace="http://www.netbeans.org/ns/ant-project-libraries/1"
23
            xmlns="http://www.netbeans.org/ns/ant-project-libraries/1"
24
            elementFormDefault="qualified">
25
    <xsd:element name="libraries">
26
        <xsd:complexType>
27
            <xsd:sequence>
28
                <xsd:element name="definitions" minOccurs="0" maxOccurs="1" type="xsd:string"/>
29
            </xsd:sequence>
30
        </xsd:complexType>
31
    </xsd:element>
32
</xsd:schema>
(-)ant/project/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java:1.45 (+54 lines)
Lines 60-65 Link Here
60
import org.netbeans.modules.project.ant.FileChangeSupport;
60
import org.netbeans.modules.project.ant.FileChangeSupport;
61
import org.netbeans.modules.project.ant.FileChangeSupportEvent;
61
import org.netbeans.modules.project.ant.FileChangeSupportEvent;
62
import org.netbeans.modules.project.ant.FileChangeSupportListener;
62
import org.netbeans.modules.project.ant.FileChangeSupportListener;
63
import org.netbeans.modules.project.ant.ProjectLibraryProvider;
63
import org.netbeans.modules.project.ant.UserQuestionHandler;
64
import org.netbeans.modules.project.ant.UserQuestionHandler;
64
import org.netbeans.modules.project.ant.Util;
65
import org.netbeans.modules.project.ant.Util;
65
import org.netbeans.spi.project.AuxiliaryConfiguration;
66
import org.netbeans.spi.project.AuxiliaryConfiguration;
Lines 1019-1024 Link Here
1019
     * It is permitted, and harmless, to include items that overlap others. For example,
1020
     * It is permitted, and harmless, to include items that overlap others. For example,
1020
     * you can have both a directory and one of its children in the include list.
1021
     * you can have both a directory and one of its children in the include list.
1021
     * </p>
1022
     * </p>
1023
     * <p>
1024
     * Whether or not you use this method, all files named <code>*-private.properties</code>
1025
     * outside the project are marked unsharable, as are such files inside the project if currently referenced
1026
     * as project libraries. (See {@link #getProjectLibrariesPropertyProvider}.)
1027
     * </p>
1022
     * <div class="nonnormative">
1028
     * <div class="nonnormative">
1023
     * <p>
1029
     * <p>
1024
     * Typical usage would be:
1030
     * Typical usage would be:
Lines 1072-1083 Link Here
1072
    public PropertyProvider getStockPropertyPreprovider() {
1078
    public PropertyProvider getStockPropertyPreprovider() {
1073
        return properties.getStockPropertyPreprovider();
1079
        return properties.getStockPropertyPreprovider();
1074
    }
1080
    }
1081
1082
    /**
1083
     * Creates a property provider which can load definitions of project libraries.
1084
     * If this project refers to any project library definition files, they will
1085
     * be included, with <code>${base}</code> replaced by the appropriate value.
1086
     * @return a property provider
1087
     * @since org.netbeans.modules.project.ant/1 1.19
1088
     * @see <a href="http://www.netbeans.org/ns/ant-project-libraries/1.xsd">Schema for project library references</a>
1089
     */
1090
    public PropertyProvider getProjectLibrariesPropertyProvider() {
1091
        return ProjectLibraryProvider.createPropertyProvider(this);
1092
    }
1093
    
1094
    /**
1095
     * Is this project shared with other or not, that is is it using shrared 
1096
     * libraries or not.
1097
     * @return <code>true</code> for shared project
1098
     * @since org.netbeans.modules.project.ant/1 1.19
1099
     */
1100
    public boolean isSharableProject()
1101
    {
1102
        return getLibrariesLocation() != null;
1103
    }
1104
1105
    /**
1106
     * Returns location of shared libraries associated with this project or null.
1107
     * @return relative or absolute OS path or null
1108
     * @since org.netbeans.modules.project.ant/1 1.19
1109
     */
1110
    public String getLibrariesLocation()
1111
    {
1112
        return ProjectLibraryProvider.getLibrariesLocationText(this.createAuxiliaryConfiguration());
1113
    }
1114
    
1115
    /**
1116
     * Change project's associated shared libraries location. If location is 
1117
     * <code>null</code> then project will not have shared libraries and will
1118
     * be considered as not being shared.
1119
     * 
1120
     * @param location project relative or absolute OS path or null
1121
     * @since org.netbeans.modules.project.ant/1 1.18
1122
     */
1123
    public void setLibrariesLocation(String location)
1124
    {
1125
        ProjectLibraryProvider.setLibrariesLocation(this, location);
1126
    }
1075
    
1127
    
1076
    /**
1128
    /**
1077
     * Get a property evaluator that can evaluate properties according to the default
1129
     * Get a property evaluator that can evaluate properties according to the default
1078
     * file layout for Ant-based projects.
1130
     * file layout for Ant-based projects.
1079
     * First, {@link #getStockPropertyPreprovider stock properties} are predefined.
1131
     * First, {@link #getStockPropertyPreprovider stock properties} are predefined.
1080
     * Then {@link #PRIVATE_PROPERTIES_PATH} is loaded via {@link #getPropertyProvider},
1132
     * Then {@link #PRIVATE_PROPERTIES_PATH} is loaded via {@link #getPropertyProvider},
1133
     * then {@link #getProjectLibrariesPropertyProvider},
1081
     * then global definitions from {@link PropertyUtils#globalPropertyProvider}
1134
     * then global definitions from {@link PropertyUtils#globalPropertyProvider}
1082
     * (though these may be overridden using the property <code>user.properties.file</code>
1135
     * (though these may be overridden using the property <code>user.properties.file</code>
1083
     * in <code>private.properties</code>), then {@link #PROJECT_PROPERTIES_PATH}.
1136
     * in <code>private.properties</code>), then {@link #PROJECT_PROPERTIES_PATH}.
Lines 1127-1132 Link Here
1127
        return PropertyUtils.resolvePath(FileUtil.toFile(dir), path);
1180
        return PropertyUtils.resolvePath(FileUtil.toFile(dir), path);
1128
    }
1181
    }
1129
    
1182
    
1183
    @Override
1130
    public String toString() {
1184
    public String toString() {
1131
        return "AntProjectHelper[" + getProjectDirectory() + "]"; // NOI18N
1185
        return "AntProjectHelper[" + getProjectDirectory() + "]"; // NOI18N
1132
    }
1186
    }
(-)ant/project/src/org/netbeans/spi/project/support/ant/ProjectGenerator.java:1.10 (-2 / +30 lines)
Lines 41-52 Link Here
41
41
42
package org.netbeans.spi.project.support.ant;
42
package org.netbeans.spi.project.support.ant;
43
43
44
import java.io.File;
44
import java.io.IOException;
45
import java.io.IOException;
45
import java.io.OutputStream;
46
import java.io.OutputStream;
46
import java.util.Iterator;
47
import java.util.Iterator;
47
import org.netbeans.api.project.Project;
48
import org.netbeans.api.project.Project;
48
import org.netbeans.api.project.ProjectManager;
49
import org.netbeans.api.project.ProjectManager;
49
import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton;
50
import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton;
51
import org.netbeans.modules.project.ant.ProjectLibraryProvider;
50
import org.openide.filesystems.FileLock;
52
import org.openide.filesystems.FileLock;
51
import org.openide.filesystems.FileObject;
53
import org.openide.filesystems.FileObject;
52
import org.openide.filesystems.FileUtil;
54
import org.openide.filesystems.FileUtil;
Lines 90-99 Link Here
90
     *                                  new project on disk is recognized by some other factory
92
     *                                  new project on disk is recognized by some other factory
91
     */
93
     */
92
    public static AntProjectHelper createProject(final FileObject directory, final String type) throws IOException, IllegalArgumentException {
94
    public static AntProjectHelper createProject(final FileObject directory, final String type) throws IOException, IllegalArgumentException {
93
        return createProject0(directory, type, null);
95
        return createProject0(directory, type, null, null);
94
    }
96
    }
95
    
97
    
96
    private static AntProjectHelper createProject0(final FileObject directory, final String type, final String name) throws IOException, IllegalArgumentException {
98
    /**
99
     * See {@link #createProject(FileObject, String)} for more datails. This 
100
     * method in addition allows to setup shared libraries location
101
     * @param directory the main project directory to create it in
102
     *                  (see {@link AntProjectHelper#getProjectDirectory})
103
     * @param type a unique project type identifier (see {@link AntBasedProjectType#getType})
104
     * @param librariesDefinition relative or absolute OS path; can be null
105
     */
106
    public static AntProjectHelper createProject(final FileObject directory, final String type, 
107
            final String librariesDefinition) throws IOException, IllegalArgumentException {
108
        return createProject0(directory, type, null, librariesDefinition);
109
    }
110
    
111
    private static AntProjectHelper createProject0(final FileObject directory, final String type, 
112
            final String name, final String librariesDefinition) throws IOException, IllegalArgumentException {
97
        try {
113
        try {
98
            return ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction<AntProjectHelper>() {
114
            return ProjectManager.mutex().writeAccess(new Mutex.ExceptionAction<AntProjectHelper>() {
99
                public AntProjectHelper run() throws IOException {
115
                public AntProjectHelper run() throws IOException {
Lines 116-121 Link Here
116
                    }
132
                    }
117
                    el = doc.createElementNS(AntProjectHelper.PROJECT_NS, "configuration"); // NOI18N
133
                    el = doc.createElementNS(AntProjectHelper.PROJECT_NS, "configuration"); // NOI18N
118
                    doc.getDocumentElement().appendChild(el);
134
                    doc.getDocumentElement().appendChild(el);
135
                    if (librariesDefinition != null) {
136
                        el.appendChild(ProjectLibraryProvider.createLibrariesElement(doc, librariesDefinition));
137
                        // create libraries property file if it does not exist:
138
                        File f = new File(librariesDefinition);
139
                        if (!f.isAbsolute()) {
140
                            f = new File(FileUtil.toFile(directory), librariesDefinition);
141
                        }
142
                        f = FileUtil.normalizeFile(f);
143
                        if (!f.exists()) {
144
                            FileUtil.createData(f);
145
                        }
146
                    }
119
                    FileLock lock = projectXml.lock();
147
                    FileLock lock = projectXml.lock();
120
                    try {
148
                    try {
121
                        OutputStream os = projectXml.getOutputStream(lock);
149
                        OutputStream os = projectXml.getOutputStream(lock);
(-)ant/project/src/org/netbeans/spi/project/support/ant/ProjectProperties.java:1.22 (+1 lines)
Lines 409-414 Link Here
409
            standardPropertyEvaluator = PropertyUtils.sequentialPropertyEvaluator(
409
            standardPropertyEvaluator = PropertyUtils.sequentialPropertyEvaluator(
410
                getStockPropertyPreprovider(),
410
                getStockPropertyPreprovider(),
411
                getPropertyProvider(AntProjectHelper.PRIVATE_PROPERTIES_PATH),
411
                getPropertyProvider(AntProjectHelper.PRIVATE_PROPERTIES_PATH),
412
                helper.getProjectLibrariesPropertyProvider(),
412
                globalProperties,
413
                globalProperties,
413
                getPropertyProvider(AntProjectHelper.PROJECT_PROPERTIES_PATH));
414
                getPropertyProvider(AntProjectHelper.PROJECT_PROPERTIES_PATH));
414
        }
415
        }
(-)ant/project/src/org/netbeans/spi/project/support/ant/ReferenceHelper.java:1.41 (-7 / +191 lines)
Lines 45-50 Link Here
45
import java.io.IOException;
45
import java.io.IOException;
46
import java.net.URI;
46
import java.net.URI;
47
import java.net.URISyntaxException;
47
import java.net.URISyntaxException;
48
import java.net.URL;
48
import java.util.ArrayList;
49
import java.util.ArrayList;
49
import java.util.Arrays;
50
import java.util.Arrays;
50
import java.util.HashMap;
51
import java.util.HashMap;
Lines 55-60 Link Here
55
import java.util.Properties;
56
import java.util.Properties;
56
import java.util.Set;
57
import java.util.Set;
57
import java.util.TreeSet;
58
import java.util.TreeSet;
59
import java.util.logging.Logger;
58
import java.util.regex.Matcher;
60
import java.util.regex.Matcher;
59
import java.util.regex.Pattern;
61
import java.util.regex.Pattern;
60
import org.netbeans.api.project.Project;
62
import org.netbeans.api.project.Project;
Lines 62-69 Link Here
62
import org.netbeans.api.project.ProjectUtils;
64
import org.netbeans.api.project.ProjectUtils;
63
import org.netbeans.api.project.ant.AntArtifact;
65
import org.netbeans.api.project.ant.AntArtifact;
64
import org.netbeans.api.project.ant.AntArtifactQuery;
66
import org.netbeans.api.project.ant.AntArtifactQuery;
67
import org.netbeans.api.project.libraries.Library;
68
import org.netbeans.api.project.libraries.LibraryChooser;
69
import org.netbeans.api.project.libraries.LibraryManager;
65
import org.netbeans.api.queries.CollocationQuery;
70
import org.netbeans.api.queries.CollocationQuery;
66
import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton;
71
import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton;
72
import org.netbeans.modules.project.ant.ProjectLibraryProvider;
67
import org.netbeans.modules.project.ant.Util;
73
import org.netbeans.modules.project.ant.Util;
68
import org.netbeans.spi.project.AuxiliaryConfiguration;
74
import org.netbeans.spi.project.AuxiliaryConfiguration;
69
import org.netbeans.spi.project.SubprojectProvider;
75
import org.netbeans.spi.project.SubprojectProvider;
Lines 72-77 Link Here
72
import org.openide.filesystems.FileUtil;
78
import org.openide.filesystems.FileUtil;
73
import org.openide.util.Mutex;
79
import org.openide.util.Mutex;
74
import org.openide.util.NbCollections;
80
import org.openide.util.NbCollections;
81
import org.openide.util.Parameters;
75
import org.openide.xml.XMLUtil;
82
import org.openide.xml.XMLUtil;
76
import org.w3c.dom.Document;
83
import org.w3c.dom.Document;
77
import org.w3c.dom.Element;
84
import org.w3c.dom.Element;
Lines 352-357 Link Here
352
            };            
359
            };            
353
        }
360
        }
354
        assert !Arrays.asList(values).contains(null) : "values=" + Arrays.toString(values) + " base=" + base + " path=" + path; // #119847
361
        assert !Arrays.asList(values).contains(null) : "values=" + Arrays.toString(values) + " base=" + base + " path=" + path; // #119847
362
        return setPathPropertyImpl(propertyName, values, propertiesFiles);
363
    }
364
    
365
    /**
366
     * Helper method which in project properties file sets up property with
367
     * the given name and with (possibly relative) path value.
368
     * @return was there any change or not
369
     */
370
    private boolean setPathProperty(File path, String propertyName) {
371
        String[] propertiesFiles = new String[] {
372
            AntProjectHelper.PROJECT_PROPERTIES_PATH
373
        };
374
        String[] values = new String[] {
375
            path.getPath()
376
        };
377
        return setPathPropertyImpl(propertyName, values, propertiesFiles);
378
    }
379
    
380
    private boolean setPathPropertyImpl(String propertyName, String[] values, String[] propertiesFiles) {
355
        
381
        
356
        boolean metadataChanged = false;
382
        boolean metadataChanged = false;
357
        for (int i=0; i<propertiesFiles.length; i++) {
383
        for (int i=0; i<propertiesFiles.length; i++) {
Lines 917-925 Link Here
917
            throw new IllegalArgumentException("Parameter file was not "+  // NOI18N
943
            throw new IllegalArgumentException("Parameter file was not "+  // NOI18N
918
                "normalized. Was "+file+" instead of "+FileUtil.normalizeFile(file));  // NOI18N
944
                "normalized. Was "+file+" instead of "+FileUtil.normalizeFile(file));  // NOI18N
919
        }
945
        }
946
        return createForeignFileReferenceImpl(file, expectedArtifactType, true);
947
    }
948
    
949
    /**
950
     * Create an Ant-interpretable string referring to a file on disk. Compared
951
     * to {@link #createForeignFileReference} the file does not have to be 
952
     * normalized (ie. it can be relative path to project base folder), no 
953
     * relativization or absolutization of path is done and
954
     * reference to file is always stored in project properties.
955
     * If the file refers to a known Ant artifact according to
956
     * {@link AntArtifactQuery#findArtifactFromFile}, of the expected type
957
     * and associated with a particular project,
958
     * the behavior is identical to {@link #createForeignFileReference(AntArtifact)}.
959
     * <p>
960
     * Acquires write access.
961
     * @param file a file to refer to (need not currently exist)
962
     * @param expectedArtifactType the required {@link AntArtifact#getType}
963
     * @return a string which can refer to that file somehow
964
     *
965
     * @since org.netbeans.modules.project.ant/1 1.19
966
     */
967
    public String createForeignFileReferenceAsIs(final File file, final String expectedArtifactType) {
968
        return createForeignFileReferenceImpl(file, expectedArtifactType, false);
969
    }
970
971
    private String createForeignFileReferenceImpl(final File file, final String expectedArtifactType, final boolean performHeuristics) {
972
        final File normalizedFile = FileUtil.normalizeFile(file);
920
        return ProjectManager.mutex().writeAccess(new Mutex.Action<String>() {
973
        return ProjectManager.mutex().writeAccess(new Mutex.Action<String>() {
921
            public String run() {
974
            public String run() {
922
                AntArtifact art = AntArtifactQuery.findArtifactFromFile(file);
975
                AntArtifact art = AntArtifactQuery.findArtifactFromFile(normalizedFile);
923
                if (art != null && art.getType().equals(expectedArtifactType) && art.getProject() != null) {
976
                if (art != null && art.getType().equals(expectedArtifactType) && art.getProject() != null) {
924
                    try {
977
                    try {
925
                        return createForeignFileReference(art);
978
                        return createForeignFileReference(art);
Lines 927-934 Link Here
927
                        throw new AssertionError(iae);
980
                        throw new AssertionError(iae);
928
                    }
981
                    }
929
                } else {
982
                } else {
930
                    String propertiesFile;
931
                    String path;
932
                    File myProjDir = FileUtil.toFile(AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory());
983
                    File myProjDir = FileUtil.toFile(AntBasedProjectFactorySingleton.getProjectFor(h).getProjectDirectory());
933
                    String fileID = file.getName();
984
                    String fileID = file.getName();
934
                    // if the file is folder then add to ID string also parent folder name,
985
                    // if the file is folder then add to ID string also parent folder name,
Lines 939-949 Link Here
939
                        fileID = file.getParentFile().getName()+"-"+file.getName();
990
                        fileID = file.getParentFile().getName()+"-"+file.getName();
940
                    }
991
                    }
941
                    fileID = PropertyUtils.getUsablePropertyName(fileID);
992
                    fileID = PropertyUtils.getUsablePropertyName(fileID);
942
                    String prop = findReferenceID(fileID, "file.reference.", file.getAbsolutePath()); // NOI18N
993
                    String prop = findReferenceID(fileID, "file.reference.", normalizedFile.getAbsolutePath()); // NOI18N
943
                    if (prop == null) {
994
                    if (prop == null) {
944
                        prop = generateUniqueID(fileID, "file.reference.", file.getAbsolutePath()); // NOI18N
995
                        prop = generateUniqueID(fileID, "file.reference.", normalizedFile.getAbsolutePath()); // NOI18N
996
                    }
997
                    if (performHeuristics) {
998
                        setPathProperty(myProjDir, normalizedFile, "file.reference." + prop);
999
                    } else {
1000
                        setPathProperty(file, "file.reference." + prop);
945
                    }
1001
                    }
946
                    setPathProperty(myProjDir, file, "file.reference." + prop);
947
                    return "${file.reference." + prop + '}'; // NOI18N
1002
                    return "${file.reference." + prop + '}'; // NOI18N
948
                }
1003
                }
949
            }
1004
            }
Lines 951-956 Link Here
951
    }
1006
    }
952
    
1007
    
953
    /**
1008
    /**
1009
     * Create an Ant-interpretable string referring to a file on disk. Compared
1010
     * to {@link #createForeignFileReference} the file does not have to be 
1011
     * normalized (ie. it can be relative path to project base folder), no 
1012
     * relativization or absolutization of path is done and
1013
     * reference to file is always stored in project properties.
1014
     * <p>
1015
     * Acquires write access.
1016
     * @param file a file to refer to (need not currently exist)
1017
     * @param fileId
1018
     * @param propertyPrefix the prefix of the created property
1019
     * @return a string which can refer to that file somehow
1020
     *
1021
     * @since org.netbeans.modules.project.ant/1 1.19
1022
     */
1023
    public String createExtraForeignFileReferenceAsIs(final File file, final String property) {
1024
        return ProjectManager.mutex().writeAccess(new Mutex.Action<String>() {
1025
            public String run() {
1026
                    setPathProperty(file, property);
1027
                    return "${" + property + '}'; // NOI18N
1028
                }
1029
        });
1030
    }
1031
    
1032
    /**
954
     * Test whether file does not lie under an extra base folder and if it does
1033
     * Test whether file does not lie under an extra base folder and if it does
955
     * then return string in form of "${extra.base}/remaining/path"; or null.
1034
     * then return string in form of "${extra.base}/remaining/path"; or null.
956
     */
1035
     */
Lines 1127-1132 Link Here
1127
    private static final Pattern FOREIGN_FILE_REFERENCE = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\.([\\d&&[^.${}]]+)\\}"); // NOI18N
1206
    private static final Pattern FOREIGN_FILE_REFERENCE = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\.([\\d&&[^.${}]]+)\\}"); // NOI18N
1128
    private static final Pattern FOREIGN_FILE_REFERENCE_OLD = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\}"); // NOI18N
1207
    private static final Pattern FOREIGN_FILE_REFERENCE_OLD = Pattern.compile("\\$\\{reference\\.([^.${}]+)\\.([^.${}]+)\\}"); // NOI18N
1129
    private static final Pattern FOREIGN_PLAIN_FILE_REFERENCE = Pattern.compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N
1208
    private static final Pattern FOREIGN_PLAIN_FILE_REFERENCE = Pattern.compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N
1209
    private static final Pattern LIBRARY_REFERENCE = Pattern.compile("\\$\\{libs\\.([^${}]+)\\.[^${}]+\\}"); // NOI18N
1130
    
1210
    
1131
    /**
1211
    /**
1132
     * Try to find an <code>AntArtifact</code> object corresponding to a given
1212
     * Try to find an <code>AntArtifact</code> object corresponding to a given
Lines 1357-1364 Link Here
1357
        h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, pub);
1437
        h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, pub);
1358
        h.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH, priv);
1438
        h.putProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH, priv);
1359
    }
1439
    }
1440
1441
    /**
1442
     * Create a reference to one volume of a library.
1443
     * @param library a library
1444
     * @param volumeType a legal volume type for that library
1445
     * @return substitutable Ant text suitable for inclusion in a properties file when also loading {@link AntProjectHelper#getProjectLibrariesPropertyProvider}
1446
     * @see #findLibrary
1447
     * @since org.netbeans.modules.project.ant/1 1.19
1448
     */
1449
    public String createLibraryReference(Library library, String volumeType) {
1450
        if (library.getManager() == LibraryManager.getDefault()) {
1451
            if (h.isSharableProject()) {
1452
                throw new IllegalArgumentException("Project ["+ // NOI18N
1453
                    h.getProjectDirectory()+
1454
                    "] is sharable and cannot reference global library "+library.getName()); // NOI18N
1455
            }
1456
        } else {
1457
            if (!ProjectLibraryProvider.isReachableLibrary(library, h)) {
1458
                throw new IllegalArgumentException("Project ["+ // NOI18N
1459
                    h.getProjectDirectory()+
1460
                    "] cannot reference a library from "+library.getManager().getLocation()); // NOI18N
1461
            }
1462
        }
1463
        return "${libs." + library.getName() + "." + volumeType + "}"; // NOI18N
1464
    }
1465
1466
    /**
1467
     * Gets a library manager corresponding to library definition file referred to from this project.
1468
     * There is no guarantee that the manager is the same object from call to call
1469
     * even if the location remain the same; in particular, it is <em>not</em> guaranteed that
1470
     * the manager match that returned from {@link Library#getManager} for libraries added
1471
     * from {@link #createLibraryReference}.
1472
     * @return a library manager associated with project's libraries or null if project is 
1473
     *  not shared (will not include {@link LibraryManager#getDefault})
1474
     * @see #createLibraryReference
1475
     * @see #findLibrary
1476
     * @since org.netbeans.modules.project.ant/1 1.19
1477
     */
1478
    public LibraryManager getProjectLibraryManager() {
1479
        return ProjectLibraryProvider.getProjectLibraryManager(h);
1480
    }
1481
1482
    /**
1483
     * Copy global IDE library to sharable libraries definition associated with
1484
     * this project. Does nothing if project is not sharable.
1485
     * 
1486
     * <p>Library creation is done under write access of ProjectManager.mutex().
1487
     * 
1488
     * @param lib global library; cannot be null
1489
     * @return newly created sharable version of library in case of sharable
1490
     *  project or given global library in case of non-sharable project
1491
     * @throws java.io.IOException if there was problem copying files
1492
     * @throws IllegalArgumentException if library is not global one or library
1493
     *  with this name already exists in sharable libraries definition
1494
     * @since org.netbeans.modules.project.ant/1 1.19
1495
     */
1496
    public Library copyLibrary(Library lib) throws IOException {
1497
        Parameters.notNull("lib", lib);
1498
        if (lib.getManager() != LibraryManager.getDefault()) {
1499
            throw new IllegalArgumentException("cannot copy non-global library "+lib.getManager().getLocation()); // NOI18N
1500
        }
1501
        if (!h.isSharableProject()) {
1502
            return lib;
1503
        }
1504
        File mainPropertiesFile = h.resolveFile(h.getLibrariesLocation());
1505
        return ProjectLibraryProvider.copyLibrary(lib, mainPropertiesFile.toURI().toURL(), false);
1506
    }
1360
    
1507
    
1361
    /**
1508
    /**
1509
     * Returns library import handler which imports global library to sharable
1510
     * one. See {@link LibraryChooser#showDialog} for usage of this handler.
1511
     * @return copy handler
1512
     * @since org.netbeans.modules.project.ant/1 1.19
1513
     */
1514
    public LibraryChooser.LibraryImportHandler getLibraryChooserImportHandler() {
1515
        return new LibraryChooser.LibraryImportHandler() {
1516
            public Library importLibrary(Library library) throws IOException {
1517
                return copyLibrary(library);
1518
            }
1519
        };        
1520
    }
1521
    /**
1522
     * Tries to find a library by name in library manager associated with the project.
1523
     * It is <em>not</em> guaranteed that any returned library is an identical object to one which passed in to {@link #createLibraryReference}.
1524
     * @param name either a bare {@link Library#getName}, or a reference as created by {@link #createLibraryReference}
1525
     * @return the first library to be found matching that name, or null if not found
1526
     * @since org.netbeans.modules.project.ant/1 1.19
1527
     */
1528
    public Library findLibrary(String name) {
1529
        Matcher m = LIBRARY_REFERENCE.matcher(name);
1530
        if (m.matches()) {
1531
            name = m.group(1);
1532
        }
1533
        LibraryManager mgr = getProjectLibraryManager();
1534
        if (mgr == null) {
1535
            return LibraryManager.getDefault().getLibrary(name);
1536
        } else {
1537
            return mgr.getLibrary(name);
1538
        }
1539
    }
1540
1541
    /**
1362
     * A raw reference descriptor representing a link to a foreign project
1542
     * A raw reference descriptor representing a link to a foreign project
1363
     * and some build artifact used from it.
1543
     * and some build artifact used from it.
1364
     * This class corresponds directly to what it stored in <code>project.xml</code>
1544
     * This class corresponds directly to what it stored in <code>project.xml</code>
Lines 1638-1644 Link Here
1638
        public String getID() {
1818
        public String getID() {
1639
            return artifactID;
1819
            return artifactID;
1640
        }
1820
        }
1641
        
1821
1822
        /**
1823
         * Get an extra properties used for target execution.
1824
         * @return a set of properties (may be empty but not null)
1825
         */
1642
        public Properties getProperties() {
1826
        public Properties getProperties() {
1643
            return props;
1827
            return props;
1644
        }
1828
        }
(-)ant/project/src/org/netbeans/spi/project/support/ant/SharabilityQueryImpl.java:1.6 (-4 / +15 lines)
Lines 47-52 Link Here
47
import java.util.ArrayList;
47
import java.util.ArrayList;
48
import java.util.List;
48
import java.util.List;
49
import org.netbeans.api.queries.SharabilityQuery;
49
import org.netbeans.api.queries.SharabilityQuery;
50
import org.netbeans.modules.project.ant.ProjectLibraryProvider;
50
import org.netbeans.spi.queries.SharabilityQueryImplementation;
51
import org.netbeans.spi.queries.SharabilityQueryImplementation;
51
import org.openide.util.WeakListeners;
52
import org.openide.util.WeakListeners;
52
53
Lines 54-60 Link Here
54
 * Standard impl of {@link SharabilityQueryImplementation}.
55
 * Standard impl of {@link SharabilityQueryImplementation}.
55
 * @author Jesse Glick
56
 * @author Jesse Glick
56
 */
57
 */
57
final class SharabilityQueryImpl implements SharabilityQueryImplementation, PropertyChangeListener {
58
final class SharabilityQueryImpl implements SharabilityQueryImplementation, PropertyChangeListener, AntProjectListener {
58
59
59
    private final AntProjectHelper h;
60
    private final AntProjectHelper h;
60
    private final PropertyEvaluator eval;
61
    private final PropertyEvaluator eval;
Lines 72-83 Link Here
72
        this.excludes = excludes;
73
        this.excludes = excludes;
73
        computeFiles();
74
        computeFiles();
74
        eval.addPropertyChangeListener(WeakListeners.propertyChange(this, eval));
75
        eval.addPropertyChangeListener(WeakListeners.propertyChange(this, eval));
76
        h.addAntProjectListener(this);
75
    }
77
    }
76
    
78
    
77
    /** Compute the absolute paths which are and are not sharable. */
79
    /** Compute the absolute paths which are and are not sharable. */
78
    private void computeFiles() {
80
    private void computeFiles() {
79
        String[] _includePaths = computeFrom(includes);
81
        String[] _includePaths = computeFrom(includes, false);
80
        String[] _excludePaths = computeFrom(excludes);
82
        String[] _excludePaths = computeFrom(excludes, true);
81
        synchronized (this) {
83
        synchronized (this) {
82
            includePaths = _includePaths;
84
            includePaths = _includePaths;
83
            excludePaths = _excludePaths;
85
            excludePaths = _excludePaths;
Lines 85-91 Link Here
85
    }
87
    }
86
    
88
    
87
    /** Compute a list of absolute paths based on some abstract names. */
89
    /** Compute a list of absolute paths based on some abstract names. */
88
    private String[] computeFrom(String[] list) {
90
    private String[] computeFrom(String[] list, boolean excludeProjectLibraryPrivate) {
89
        List<String> result = new ArrayList<String>(list.length);
91
        List<String> result = new ArrayList<String>(list.length);
90
        for (String s : list) {
92
        for (String s : list) {
91
            String val = eval.evaluate(s);
93
            String val = eval.evaluate(s);
Lines 94-99 Link Here
94
                result.add(f.getAbsolutePath());
96
                result.add(f.getAbsolutePath());
95
            }
97
            }
96
        }
98
        }
99
        if (excludeProjectLibraryPrivate) {
100
            result.addAll(ProjectLibraryProvider.getUnsharablePathsWithinProject(h));
101
        }
97
        // XXX should remove overlaps somehow
102
        // XXX should remove overlaps somehow
98
        return result.toArray(new String[result.size()]);
103
        return result.toArray(new String[result.size()]);
99
    }
104
    }
Lines 132-136 Link Here
132
    public void propertyChange(PropertyChangeEvent evt) {
137
    public void propertyChange(PropertyChangeEvent evt) {
133
        computeFiles();
138
        computeFiles();
134
    }
139
    }
140
141
    public void configurationXmlChanged(AntProjectEvent ev) {
142
        computeFiles();
143
    }
144
145
    public void propertiesChanged(AntProjectEvent ev) {}
135
    
146
    
136
}
147
}
(-)/dev/null (+129 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5
 * 
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 * 
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 * 
35
 * Contributor(s):
36
 * 
37
 * Portions Copyrighted 2007 Sun Microsystems, Inc.
38
 */
39
40
package org.netbeans.modules.project.ant;
41
42
import org.netbeans.modules.project.ant.*;
43
import org.netbeans.modules.project.ant.FileOwnerCollocationQueryImpl;
44
import java.io.File;
45
import java.io.IOException;
46
import org.netbeans.api.project.TestUtil;
47
import org.netbeans.junit.NbTestCase;
48
import org.openide.filesystems.FileObject;
49
import org.openide.filesystems.FileUtil;
50
import org.openide.util.test.MockLookup;
51
52
/**
53
 *
54
 * @author mkleint
55
 */
56
public class FileOwnerCollocationQueryImplTest extends NbTestCase {
57
    
58
    public FileOwnerCollocationQueryImplTest(String testName) {
59
        super(testName);
60
    }            
61
    private FileObject scratch;
62
63
    @Override
64
    protected void setUp() throws Exception {
65
        super.setUp();
66
        MockLookup.setInstances(TestUtil.testProjectFactory());
67
        scratch = TestUtil.makeScratchDir(this);
68
    }
69
70
    @Override
71
    protected void tearDown() throws Exception {
72
        super.tearDown();
73
    }
74
75
    /**
76
     * Test of findRoot method, of class FileOwnerCollocationQueryImpl.
77
     */
78
    public void testFindRoot() throws IOException {
79
        FileObject root =  scratch.createFolder("root");
80
        FileObject projdir = root.createFolder("prj1");
81
        projdir.createFolder("testproject");
82
        
83
        //root/prj1/foo
84
        FileOwnerCollocationQueryImpl instance = new FileOwnerCollocationQueryImpl();
85
        assertEquals(instance.findRoot(FileUtil.toFile(projdir.createData("foo"))), FileUtil.toFile(root));
86
        
87
        //root/prj2/foo/prj3/bar
88
        projdir = root.createFolder("prj2");
89
        projdir.createFolder("testproject");
90
        projdir = projdir.createFolder("foo").createFolder("prj3");
91
        projdir.createFolder("testproject");
92
        assertEquals(instance.findRoot(FileUtil.toFile(projdir.createData("bar"))), FileUtil.toFile(root));
93
        
94
        //root
95
        assertEquals(instance.findRoot(FileUtil.toFile(root)), null);
96
    }
97
98
    /**
99
     * Test of areCollocated method, of class FileOwnerCollocationQueryImpl.
100
     */
101
    public void testAreCollocated() throws IOException {
102
        FileObject root =  scratch.createFolder("root");
103
        FileObject projdir = scratch.createFolder("prj1");
104
        projdir.createFolder("testproject");
105
        FileObject lib = root.createFolder("libs");
106
107
        
108
        File file1 = FileUtil.toFile(lib.createData("pron"));
109
        File file2 = FileUtil.toFile(projdir.createData("xxx"));
110
        FileOwnerCollocationQueryImpl instance = new FileOwnerCollocationQueryImpl();
111
        assertTrue(instance.areCollocated(file1, file2));
112
        
113
        file1 = FileUtil.toFile(projdir);
114
        file2 = FileUtil.toFile(lib);
115
        assertTrue(instance.areCollocated(file1, file2));
116
        
117
        projdir = root.createFolder("noproj").createFolder("proj1");
118
        projdir.createFolder("testproject");
119
        FileObject projdir2 = root.createFolder("noproj2").createFolder("proj2");
120
        projdir2.createFolder("testproject");
121
        file1 = FileUtil.toFile(projdir.createData("foo"));
122
        file2 = FileUtil.toFile(projdir2.createData("bar"));
123
//        System.out.println("root1=" + instance.findRoot(file1));
124
//        System.out.println("root2=" + instance.findRoot(file2));
125
        assertFalse(instance.areCollocated(file1, file2));
126
        
127
    }
128
129
}
(-)/dev/null (+468 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.netbeans.modules.project.ant;
21
22
import java.beans.PropertyChangeListener;
23
import java.beans.PropertyChangeSupport;
24
import java.io.File;
25
import java.io.FileInputStream;
26
import java.io.FileOutputStream;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.OutputStream;
30
import java.lang.reflect.Method;
31
import java.net.URL;
32
import java.util.ArrayList;
33
import java.util.Arrays;
34
import java.util.Collections;
35
import java.util.HashMap;
36
import java.util.List;
37
import java.util.Map;
38
import java.util.Properties;
39
import java.util.StringTokenizer;
40
import java.util.TreeMap;
41
import java.util.jar.JarFile;
42
import java.util.zip.ZipEntry;
43
import java.util.zip.ZipOutputStream;
44
import org.netbeans.api.project.Project;
45
import org.netbeans.api.project.ProjectManager;
46
import org.netbeans.api.project.TestUtil;
47
import org.netbeans.api.project.libraries.Library;
48
import org.netbeans.api.project.libraries.LibraryManager;
49
import org.netbeans.api.project.ui.OpenProjects;
50
import org.netbeans.api.queries.SharabilityQuery;
51
import org.netbeans.junit.NbTestCase;
52
import org.netbeans.spi.project.AuxiliaryConfiguration;
53
import org.netbeans.spi.project.libraries.LibraryImplementation;
54
import org.netbeans.spi.project.libraries.LibraryProvider;
55
import org.netbeans.spi.project.libraries.LibraryTypeProvider;
56
import org.netbeans.spi.project.libraries.support.LibrariesSupport;
57
import org.netbeans.spi.project.support.ant.AntBasedTestUtil;
58
import org.netbeans.spi.project.support.ant.AntProjectHelper;
59
import org.netbeans.spi.project.support.ant.EditableProperties;
60
import org.netbeans.spi.project.support.ant.ProjectGenerator;
61
import org.netbeans.spi.project.support.ant.PropertyProvider;
62
import org.netbeans.spi.queries.CollocationQueryImplementation;
63
import org.openide.filesystems.FileObject;
64
import org.openide.filesystems.FileUtil;
65
import org.openide.filesystems.Repository;
66
import org.openide.loaders.DataFolder;
67
import org.openide.loaders.InstanceDataObject;
68
import org.openide.util.NbCollections;
69
import org.openide.util.lookup.Lookups;
70
import org.openide.util.test.MockChangeListener;
71
import org.openide.util.test.MockLookup;
72
import org.openide.util.test.MockPropertyChangeListener;
73
import org.openide.xml.XMLUtil;
74
import org.w3c.dom.Document;
75
import org.w3c.dom.Element;
76
77
public class ProjectLibraryProviderTest extends NbTestCase {
78
79
    public ProjectLibraryProviderTest(String name) {
80
        super(name);
81
    }
82
83
    private FileObject projdir;
84
    private AntProjectHelper helper;
85
    private Project project;
86
    private URL base;
87
    private TestLibraryProvider libraryProvider;
88
    
89
90
    @Override
91
    protected void setUp() throws Exception {
92
        super.setUp();
93
        libraryProvider = new TestLibraryProvider();
94
        MockLookup.setLookup(Lookups.fixed(AntBasedTestUtil.testAntBasedProjectType(), AntBasedTestUtil.testCollocationQueryImplementation(getWorkDir()), libraryProvider),
95
                // Filter out standard CQIs since they are bogus.
96
                Lookups.exclude(Lookups.metaInfServices(ProjectLibraryProviderTest.class.getClassLoader()), CollocationQueryImplementation.class));
97
        projdir = TestUtil.makeScratchDir(this).createFolder("prj");
98
        helper = ProjectGenerator.createProject(projdir, "test");
99
        project = ProjectManager.getDefault().findProject(projdir);
100
        close(OpenProjects.getDefault().getOpenProjects());
101
        base = getWorkDir().toURI().toURL();
102
        ProjectLibraryProvider.FIRE_CHANGES_SYNCH = true;
103
        registerTestLibraryTypeProvider();
104
    }
105
106
    // XXX test name/type/description
107
    // XXX test : vs. ; and / vs. \ (in <definitions> and in *.properties)
108
    // XXX test set name, description
109
110
    public void testLibraryLoadingBasic() throws Exception {
111
        writeProperties("libs/my libraries.properties",
112
                "libs.jgraph.classpath=${base}/jgraph.jar:${base}/../extra libs/jgraph-extras.jar",
113
                "libs.jgraph.javadoc=${base}/api/jgraph-docs",
114
                "irrelevant=stuff");
115
        storeDefs(project, "../libs/my libraries.properties");
116
        Library lib = LibraryManager.forLocation(new URL(base, "libs/my%20libraries.properties")).getLibrary("jgraph");
117
        assertNotNull(lib);
118
        assertEquals("jgraph", lib.getName());
119
        assertEquals("jgraph", lib.getDisplayName());
120
        assertNull(lib.getDescription());
121
        assertEquals("j2se", lib.getType());
122
        assertEquals(Arrays.asList(new URL("jar:file:jgraph.jar!/"), new URL("jar:file:../extra%20libs/jgraph-extras.jar!/")), lib.getContent("classpath"));
123
        assertEquals(Collections.singletonList(new URL("file:api/jgraph-docs/")), lib.getContent("javadoc"));
124
        assertEquals(Collections.emptyList(), lib.getContent("src"));
125
    }
126
127
    public void testLibraryLoadingPrivateAbsolute() throws Exception {
128
        writeProperties("libs/libraries.properties",
129
                "libs.jgraph.classpath=${base}/jgraph.jar");
130
        writeProperties("libs/libraries-private.properties",
131
                "libs.jgraph.src=" + new File(getWorkDir(), "jgraph-src.zip"),
132
                "libs.jgraph.javadoc=" + new File(getWorkDir(), "jgraph-api"));
133
        storeDefs(project, "../libs/libraries.properties");
134
        Library lib = LibraryManager.forLocation(new URL(base, "libs/libraries.properties")).getLibrary("jgraph");
135
        assertEquals(Collections.singletonList(new URL("jar:file:jgraph.jar!/")), lib.getContent("classpath"));
136
        assertEquals(Collections.singletonList(new URL("jar:" + base + "jgraph-src.zip!/")), lib.getContent("src"));
137
        assertEquals(Collections.singletonList(new URL(base, "jgraph-api/")), lib.getContent("javadoc"));
138
    }
139
140
    public void testPrivateOverridesSharedProperties() throws Exception {
141
        writeProperties("libs/libraries.properties",
142
                "libs.jgraph.classpath=${base}/jgraph.jar");
143
        writeProperties("libs/libraries-private.properties",
144
                "libs.jgraph.classpath=" + new File(getWorkDir(), "jgraph-api"));
145
        storeDefs(project, "../libs/libraries.properties");
146
        Library lib = LibraryManager.forLocation(new URL(base, "libs/libraries.properties")).getLibrary("jgraph");
147
        assertEquals(Collections.singletonList(new URL(base, "jgraph-api/")), lib.getContent("classpath"));
148
    }
149
150
    public void testSetContent() throws Exception {
151
        writeProperties("libs/libraries.properties",
152
                "libs.jgraph.classpath=");
153
        storeDefs(project, "../libs/libraries.properties");
154
        Library lib = LibraryManager.forLocation(new URL(base, "libs/libraries.properties")).getLibrary("jgraph");
155
        setLibraryContent(lib, "classpath", new URL("jar:file:jgraph.jar!/"), new URL("jar:file:../extra%20libs/jgraph-extras.jar!/"));
156
        setLibraryContent(lib, "src", new URL(base, "separate/jgraph-src/"), new URL(base, "jgraph-other-src/"));
157
        setLibraryContent(lib, "javadoc", new URL("jar:" + base + "separate/jgraph-api.zip!/"));
158
        Map<String,String> m = new HashMap<String,String>();
159
        File separate = new File(getWorkDir(), "separate");
160
        m.put("libs.jgraph.classpath", "${base}/jgraph.jar"+File.pathSeparatorChar+"${base}/../extra libs/jgraph-extras.jar");
161
        m.put("libs.jgraph.src", new File(separate, "jgraph-src").getAbsolutePath().replace('\\', '/') + File.pathSeparator + 
162
                new File(getWorkDir(), "jgraph-other-src").getAbsolutePath().replace('\\', '/'));
163
        m.put("libs.jgraph.javadoc", new File(separate, "jgraph-api.zip").getAbsolutePath().replace('\\', '/'));
164
        assertEquals(m, loadProperties("libs/libraries.properties"));
165
    }
166
167
    public void testAreaChangesFromProjectsOpenedClosed() throws Exception {
168
        storeDefs(project, "../libraries.properties");
169
        assertEquals("[<none>]", openedLibraryManagers());
170
        open(project);
171
        assertEquals("[<none>, " + base + "libraries.properties]", openedLibraryManagers());
172
        close(project);
173
        assertEquals("[<none>]", openedLibraryManagers());
174
        Project project2 = ProjectManager.getDefault().findProject(
175
                ProjectGenerator.createProject(projdir.getParent().createFolder("prj2"), "test").getProjectDirectory());
176
        storeDefs(project2, "../lib2.properties");
177
        open(project, project2);
178
        assertEquals("[<none>, " + base + "lib2.properties, "+ base + "libraries.properties]", openedLibraryManagers());
179
        close(project);
180
        assertEquals("[<none>, " + base + "lib2.properties]", openedLibraryManagers());
181
    }
182
    private static String openedLibraryManagers() {
183
        List<String> urls = new ArrayList<String>();
184
        for (LibraryManager mgr : LibraryManager.getOpenManagers()) {
185
            URL u = mgr.getLocation();
186
            urls.add(u != null ? u.toExternalForm() : "<none>");
187
        }
188
        Collections.sort(urls);
189
        return urls.toString();
190
    }
191
192
    public void testChangesLibraries() throws Exception {
193
        PropertyProvider pp = helper.getProjectLibrariesPropertyProvider();
194
        writeProperties("libraries.properties",
195
                "libs.jgraph.classpath=");
196
        storeDefs(project, "../libraries.properties");
197
        LibraryManager mgr = LibraryManager.forLocation(new URL(base, "libraries.properties"));
198
        Library lib1 = mgr.getLibrary("jgraph");
199
        assertEquals(Collections.emptyList(), lib1.getContent("classpath"));
200
        assertEquals("{libs.jgraph.classpath=}", new TreeMap<String,String>(pp.getProperties()).toString());
201
        MockPropertyChangeListener liblist = new MockPropertyChangeListener(LibraryManager.PROP_LIBRARIES);
202
        MockPropertyChangeListener contentlist = new MockPropertyChangeListener(Library.PROP_CONTENT);
203
        mgr.addPropertyChangeListener(liblist);
204
        lib1.addPropertyChangeListener(contentlist);
205
        MockChangeListener pplist = new MockChangeListener();
206
        pp.addChangeListener(pplist);
207
        writeProperties("libraries.properties",
208
                "libs.jgraph.classpath=${base}/jgraph",
209
                "libs.collections.classpath=${base}/collections");
210
        contentlist.assertEventCount(1);
211
        assertEquals(Collections.singletonList(new URL("file:jgraph/")), lib1.getContent("classpath"));
212
        liblist.assertEventCount(1);
213
        assertEquals(lib1, mgr.getLibrary("jgraph"));
214
        Library lib2 = mgr.getLibrary("collections");
215
        assertEquals(Collections.singletonList(new URL("file:collections/")), lib2.getContent("classpath"));
216
        pplist.assertEventCount(1);
217
        assertEquals(("{libs.collections.classpath=" + getWorkDir() + "/collections, libs.jgraph.classpath=" + 
218
                getWorkDir() + "/jgraph}").replace('/', File.separatorChar),
219
                new TreeMap<String,String>(pp.getProperties()).toString());
220
        writeProperties("others.properties",
221
                "libs.jrcs.classpath=");
222
        storeDefs(project, "../others.properties");
223
        contentlist.assertEventCount(0);
224
        liblist.assertEventCount(0);
225
        pplist.assertEventCount(1);
226
        assertEquals(("{libs.jrcs.classpath=}").replace('/', File.separatorChar),
227
                new TreeMap<String,String>(pp.getProperties()).toString());
228
    }
229
230
    public void testCreateRemoveLibrary() throws Exception {
231
        LibraryManager mgr = LibraryManager.forLocation(new URL(base, "libraries.properties"));
232
        Map<String,List<URL>> content = new HashMap<String,List<URL>>();
233
        content.put("classpath", Arrays.asList(new URL("jar:file:jh.jar!/"), new URL("jar:file:jh-search.jar!/")));
234
        content.put("javadoc", Arrays.asList(new URL("file:jh-api/")));
235
        Library lib = mgr.createLibrary("j2se", "javahelp", content);
236
        assertEquals("j2se", lib.getType());
237
        assertEquals("javahelp", lib.getName());
238
        assertEquals(content.get("classpath"), lib.getContent("classpath"));
239
        assertEquals(content.get("javadoc"), lib.getContent("javadoc"));
240
        lib = mgr.createLibrary("j2me", "gps", Collections.<String,List<URL>>emptyMap());
241
        assertEquals("j2me", lib.getType());
242
        assertEquals("gps", lib.getName());
243
        Map<String,String> expected = new HashMap<String,String>();
244
        expected.put("libs.javahelp.classpath", "${base}/jh.jar"+File.pathSeparatorChar+"${base}/jh-search.jar");
245
        expected.put("libs.javahelp.javadoc", "${base}/jh-api");
246
        expected.put("libs.gps.type", "j2me");
247
        assertEquals(expected, loadProperties("libraries.properties"));
248
        mgr.removeLibrary(lib);
249
        expected.remove("libs.gps.type");
250
        assertEquals(expected, loadProperties("libraries.properties"));
251
    }
252
253
    public void testPropertyProviderBasic() throws Exception {
254
        writeProperties("libs/libraries.properties",
255
                "libs.jgraph.classpath=${base}/jgraph.jar:${base}/../extralibs/jgraph-extras.jar");
256
        storeDefs(project, "../libs/libraries.properties");
257
        PropertyProvider pp = helper.getProjectLibrariesPropertyProvider();
258
        assertEquals(Collections.singletonMap("libs.jgraph.classpath", (getWorkDir() + "/libs/jgraph.jar:" + 
259
            getWorkDir() + "/libs/../extralibs/jgraph-extras.jar").replace('/', File.separatorChar)), pp.getProperties());
260
    }
261
262
    public void testPropertyProviderPrivateAbsolute() throws Exception {
263
        writeProperties("libs/libraries.properties",
264
                "libs.jgraph.classpath=${base}/jgraph-1.0.jar");
265
        writeProperties("libs/libraries-private.properties",
266
                "libs.jgraph.classpath=" + new File(getWorkDir(), "jgraph-2.0-beta.jar"));
267
        storeDefs(project, "../libs/libraries.properties");
268
        PropertyProvider pp = helper.getProjectLibrariesPropertyProvider();
269
        assertEquals(Collections.singletonMap("libs.jgraph.classpath", new File(getWorkDir(), "jgraph-2.0-beta.jar").getAbsolutePath()), pp.getProperties());
270
    }
271
272
    public void testSharability() throws Exception {
273
        assertSharability(SharabilityQuery.UNKNOWN, "libs/index.properties");
274
        assertSharability(SharabilityQuery.NOT_SHARABLE, "libs/index-private.properties");
275
        assertSharability(SharabilityQuery.SHARABLE, "prj/libs/index.properties");
276
        assertSharability(SharabilityQuery.NOT_SHARABLE, "prj/libs/index-private.properties");
277
        assertSharability(SharabilityQuery.SHARABLE, "prj/libs/");
278
        storeDefs(project, "libs/index.properties");
279
        assertSharability(SharabilityQuery.SHARABLE, "prj/libs/index.properties");
280
        assertSharability(SharabilityQuery.NOT_SHARABLE, "prj/libs/index-private.properties");
281
        assertSharability(SharabilityQuery.MIXED, "prj/libs/");
282
    }
283
    private void assertSharability(int mode, String path) throws Exception {
284
        File f = new File(getWorkDir(), path.replace('/', File.separatorChar));
285
        if (path.endsWith("/")) {
286
            FileUtil.createFolder(f);
287
        } else {
288
            FileUtil.createData(f);
289
        }
290
        assertEquals(mode, SharabilityQuery.getSharability(f));
291
    }
292
293
    private void writeProperties(String path, String... properties) throws IOException {
294
        FileObject f = FileUtil.createData(FileUtil.toFileObject(getWorkDir()), path);
295
        EditableProperties ep = new EditableProperties();
296
        for (String def : properties) {
297
            String[] nameValue = def.split("=", 2);
298
            ep.put(nameValue[0], nameValue[1]);
299
        }
300
        OutputStream os = f.getOutputStream();
301
        ep.store(os);
302
        os.close();
303
    }
304
305
    private static void storeDefs(Project project, String... definitions) throws IOException {
306
        Document doc = XMLUtil.createDocument("x", null, null, null);
307
        Element libraries = doc.createElementNS("http://www.netbeans.org/ns/ant-project-libraries/1", "libraries");
308
        for (String def : definitions) {
309
            libraries.appendChild(doc.createElementNS("http://www.netbeans.org/ns/ant-project-libraries/1", "definitions")).appendChild(doc.createTextNode(def));
310
        }
311
        project.getLookup().lookup(AuxiliaryConfiguration.class).putConfigurationFragment(libraries, true);
312
        ProjectManager.getDefault().saveProject(project); // to assist in debugging
313
    }
314
315
    private static void open(Project... projects) {
316
        OpenProjects.getDefault().open(projects, false);
317
    }
318
319
    private static void close(Project... projects) {
320
        OpenProjects.getDefault().close(projects);
321
    }
322
323
    private static void setLibraryContent(Library lib, String volumeType, URL... paths) throws Exception {
324
        MockPropertyChangeListener l = new MockPropertyChangeListener(Library.PROP_CONTENT);
325
        lib.addPropertyChangeListener(l);
326
        LibraryImplementation impl = getLibraryImplementation(lib);
327
        List<URL> path = Arrays.asList(paths);
328
        impl.setContent(volumeType, path);
329
        l.assertEventCount(1);
330
        assertEquals(path, lib.getContent(volumeType));
331
    }
332
333
    private static LibraryImplementation getLibraryImplementation(Library lib) throws Exception {
334
        Method getLibraryImplementation = Library.class.getDeclaredMethod("getLibraryImplementation");
335
        getLibraryImplementation.setAccessible(true);
336
        return (LibraryImplementation) getLibraryImplementation.invoke(lib);
337
    }
338
339
    private Map<String,String> loadProperties(String path) throws IOException {
340
        File f = new File(getWorkDir(), path.replace('/', File.separatorChar));
341
        if (!f.isFile()) {
342
            return Collections.emptyMap();
343
        }
344
        Properties p = new Properties();
345
        InputStream is = new FileInputStream(f);
346
        p.load(is);
347
        is.close();
348
        return NbCollections.checkedMapByFilter(p, String.class, String.class, true);
349
    }
350
351
    /**
352
     * Test of copyLibrary method, of class LibrariesSupport.
353
     */
354
    public void testCopyLibrary() throws Exception {
355
        File f = new File(this.getWorkDir(), "bertie.jar");
356
        createFakeJAR(f);
357
        File f1 = new File(this.getWorkDir(), "dog.jar");
358
        createFakeJAR(f1);
359
        new File(this.getWorkDir(), "sources").mkdir();
360
        File f2 = new File(this.getWorkDir(), "sources/bertie.jar");
361
        createFakeJAR(f2);
362
        new File(this.getWorkDir(), "libraries").mkdir();
363
        File f3 = new File(this.getWorkDir(), "libraries/libs.properties");
364
        f3.createNewFile();
365
        LibraryImplementation l1 = LibrariesSupport.createLibraryImplementation("j2test", new String[]{"jars", "sources"});
366
        l1.setName("vino");
367
        l1.setContent("jars", Arrays.asList(new URL[]{f.toURI().toURL(), f1.toURI().toURL()}));
368
        l1.setContent("sources", Arrays.asList(new URL[]{f2.toURI().toURL()}));
369
        libraryProvider.set(l1);
370
        Library l = LibraryManager.getDefault().getLibrary("vino");
371
        assertNotNull(l);
372
        assertEquals(LibraryManager.getDefault(), l.getManager());
373
        URL u = f3.toURI().toURL();
374
        Library result = ProjectLibraryProvider.copyLibrary(l, u, false);
375
        assertNotNull(result);
376
        assertEquals(u, result.getManager().getLocation());
377
        assertEquals(Arrays.asList(new URL("jar:file:vino/bertie.jar!/"),
378
                new URL("jar:file:vino/dog.jar!/")), result.getContent("jars"));
379
        assertEquals(Arrays.asList(new URL("jar:file:vino/bertie-2.jar!/")), result.getContent("sources"));
380
        assertEquals("vino", result.getName());
381
        assertEquals("j2test", result.getType());
382
        assertEquals(new File(this.getWorkDir(), "libraries/vino/bertie.jar").getPath(), 
383
                FileUtil.toFile(LibrariesSupport.resolveLibraryEntryFileObject(u, FileUtil.getArchiveFile(result.getContent("jars").get(0)))).getPath());
384
    }
385
    
386
    private void createFakeJAR(File f) throws IOException {
387
        // create just enough to make URLMapper recognize file as JAR:
388
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f));
389
        zos.putNextEntry(new ZipEntry("test"));
390
        zos.finish();
391
        zos.close();
392
    }
393
394
    public static class TestLibraryProvider implements LibraryProvider<LibraryImplementation> {
395
396
        public final List<LibraryImplementation> libs = new ArrayList<LibraryImplementation>();
397
        final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
398
399
        public void set(LibraryImplementation... nue) {
400
            libs.clear();
401
            libs.addAll(Arrays.asList(nue));
402
            pcs.firePropertyChange(PROP_LIBRARIES, null, null);
403
        }
404
405
        public LibraryImplementation[] getLibraries() {
406
            return libs.toArray(new LibraryImplementation[0]);
407
        }
408
409
        public void addPropertyChangeListener(PropertyChangeListener listener) {
410
            pcs.addPropertyChangeListener(listener);
411
        }
412
413
        public void removePropertyChangeListener(PropertyChangeListener listener) {
414
            pcs.removePropertyChangeListener(listener);
415
        }
416
417
    }
418
419
    public static class TestLibraryTypeProvider implements LibraryTypeProvider {
420
421
422
        public String getDisplayName() {
423
            return "j2test";
424
        }
425
426
        public String getLibraryType() {
427
            return "j2test";
428
        }
429
430
        public String[] getSupportedVolumeTypes() {
431
            return new String[] {"jars","sources"};
432
        }
433
434
        public LibraryImplementation createLibrary() {
435
            return LibrariesSupport.createLibraryImplementation("j2test", new String[] {"jars","sources"});
436
        }
437
438
        public void libraryDeleted(LibraryImplementation library) {
439
        }
440
441
        public void libraryCreated(LibraryImplementation library) {
442
        }
443
444
        public java.beans.Customizer getCustomizer(String volumeType) {
445
            return null;
446
        }
447
448
        public org.openide.util.Lookup getLookup() {
449
            return null;
450
        }
451
    }
452
    
453
    private static void registerTestLibraryTypeProvider () throws Exception {
454
        StringTokenizer tk = new StringTokenizer("org-netbeans-api-project-libraries/LibraryTypeProviders","/");
455
        FileObject root = Repository.getDefault().getDefaultFileSystem().getRoot();
456
        while (tk.hasMoreElements()) {
457
            String pathElement = tk.nextToken();
458
            FileObject tmp = root.getFileObject(pathElement);
459
            if (tmp == null) {
460
                tmp = root.createFolder(pathElement);
461
            }
462
            root = tmp;
463
        }
464
        if (root.getChildren().length == 0) {
465
            InstanceDataObject.create (DataFolder.findFolder(root),"TestLibraryTypeProvider",TestLibraryTypeProvider.class);
466
        }
467
    }
468
}
(-)ant/project/test/unit/src/org/netbeans/spi/project/support/ant/AntBasedTestUtil.java:1.25 (-2 / +8 lines)
Lines 104-110 Link Here
104
     * with namespaces <samp>urn:test:shared</samp> and <samp>urn:test:private</samp>.
104
     * with namespaces <samp>urn:test:shared</samp> and <samp>urn:test:private</samp>.
105
     * Loading the project succeeds unless there is a file in it <samp>nbproject/broken</samp>.
105
     * Loading the project succeeds unless there is a file in it <samp>nbproject/broken</samp>.
106
     * The project's methods mostly delegate to the helper; its lookup uses the helper's
106
     * The project's methods mostly delegate to the helper; its lookup uses the helper's
107
     * supports for ExtensibleMetadataProvider, ActionProvider, and SubprojectProvider,
107
     * supports for AuxiliaryConfiguration, CacheDirectoryProvider, and SubprojectProvider,
108
     * and also adds an instance of String, namely "hello".
108
     * and also adds an instance of String, namely "hello".
109
     * It also puts the AntProjectHelper into its lookup to assist in testing.
109
     * It also puts the AntProjectHelper into its lookup to assist in testing.
110
     * <code>build-impl.xml</code> is generated from <code>data/build-impl.xsl</code>
110
     * <code>build-impl.xml</code> is generated from <code>data/build-impl.xsl</code>
Lines 188-193 Link Here
188
                genFilesHelper,
188
                genFilesHelper,
189
                aux,
189
                aux,
190
                helper.createCacheDirectoryProvider(),
190
                helper.createCacheDirectoryProvider(),
191
                helper.createSharabilityQuery(helper.getStandardPropertyEvaluator(), new String[0], new String[0]),
191
                refHelper.createSubprojectProvider(),
192
                refHelper.createSubprojectProvider(),
192
                new TestAntArtifactProvider(),
193
                new TestAntArtifactProvider(),
193
                new ProjectXmlSavedHook() {
194
                new ProjectXmlSavedHook() {
Lines 436-442 Link Here
436
    /**
437
    /**
437
     * Get a sample file collocation query provider.
438
     * Get a sample file collocation query provider.
438
     * Files under the supplied root are normally considered to be collocated.
439
     * Files under the supplied root are normally considered to be collocated.
439
     * However the subdirectory <samp>separate<samp> (if it exists) forms its own root.
440
     * However the subdirectory <samp>separate</samp> (if it exists) forms its own root.
440
     * And the subdirectory <samp>transient</samp> (if it exists) does not form a root,
441
     * And the subdirectory <samp>transient</samp> (if it exists) does not form a root,
441
     * but any files in there are not considered collocated with anything.
442
     * but any files in there are not considered collocated with anything.
442
     */
443
     */
Lines 481-486 Link Here
481
                return null;
482
                return null;
482
            }
483
            }
483
            return root;
484
            return root;
485
        }
486
487
        @Override
488
        public String toString() {
489
            return "TestCollocationQueryImplementation[" + root + "]";
484
        }
490
        }
485
        
491
        
486
    }
492
    }
(-)ant/project/test/unit/src/org/netbeans/spi/project/support/ant/ReferenceHelperTest.java:1.27 (-12 / +72 lines)
Lines 44-51 Link Here
44
import java.io.File;
44
import java.io.File;
45
import java.io.OutputStream;
45
import java.io.OutputStream;
46
import java.net.URI;
46
import java.net.URI;
47
import java.net.URL;
47
import java.util.ArrayList;
48
import java.util.ArrayList;
48
import java.util.Arrays;
49
import java.util.Arrays;
50
import java.util.Collection;
49
import java.util.Collections;
51
import java.util.Collections;
50
import java.util.List;
52
import java.util.List;
51
import java.util.Properties;
53
import java.util.Properties;
Lines 57-64 Link Here
57
import org.netbeans.api.project.TestUtil;
59
import org.netbeans.api.project.TestUtil;
58
import org.netbeans.api.project.ant.AntArtifact;
60
import org.netbeans.api.project.ant.AntArtifact;
59
import org.netbeans.api.project.ant.AntArtifactQuery;
61
import org.netbeans.api.project.ant.AntArtifactQuery;
62
import org.netbeans.api.project.libraries.Library;
63
import org.netbeans.api.project.libraries.LibraryManager;
60
import org.netbeans.api.queries.CollocationQuery;
64
import org.netbeans.api.queries.CollocationQuery;
61
import org.netbeans.junit.NbTestCase;
65
import org.netbeans.junit.NbTestCase;
66
import org.netbeans.modules.project.ant.ProjectLibraryProvider;
62
import org.netbeans.modules.project.ant.Util;
67
import org.netbeans.modules.project.ant.Util;
63
import org.netbeans.modules.queries.AlwaysRelativeCollocationQuery;
68
import org.netbeans.modules.queries.AlwaysRelativeCollocationQuery;
64
import org.netbeans.spi.project.AuxiliaryConfiguration;
69
import org.netbeans.spi.project.AuxiliaryConfiguration;
Lines 66-74 Link Here
66
import org.openide.filesystems.FileLock;
71
import org.openide.filesystems.FileLock;
67
import org.openide.filesystems.FileObject;
72
import org.openide.filesystems.FileObject;
68
import org.openide.filesystems.FileUtil;
73
import org.openide.filesystems.FileUtil;
69
import org.openide.util.Lookup;
70
import org.openide.util.lookup.Lookups;
74
import org.openide.util.lookup.Lookups;
71
import org.openide.util.lookup.ProxyLookup;
75
import org.openide.util.test.MockLookup;
72
import org.w3c.dom.Document;
76
import org.w3c.dom.Document;
73
import org.w3c.dom.Element;
77
import org.w3c.dom.Element;
74
import org.w3c.dom.NodeList;
78
import org.w3c.dom.NodeList;
Lines 148-164 Link Here
148
    }
152
    }
149
    
153
    
150
    protected void setUp() throws Exception {
154
    protected void setUp() throws Exception {
151
        super.setUp();        
155
        super.setUp();
152
        Object[] instances = new Object[] {
153
            AntBasedTestUtil.testAntBasedProjectType(),
154
            AntBasedTestUtil.testCollocationQueryImplementation(getWorkDir()),
155
        };
156
        ClassLoader l = ReferenceHelper.class.getClassLoader();
156
        ClassLoader l = ReferenceHelper.class.getClassLoader();
157
        TestUtil.setLookup (new ProxyLookup (new Lookup[] {
157
        MockLookup.setLookup(
158
            Lookups.fixed(instances),
158
            Lookups.fixed(AntBasedTestUtil.testAntBasedProjectType(), AntBasedTestUtil.testCollocationQueryImplementation(getWorkDir())),
159
            Lookups.singleton(l),
159
            Lookups.singleton(l),
160
            Lookups.exclude(Lookups.metaInfServices(l), new Class[] {AlwaysRelativeCollocationQuery.class})
160
            Lookups.exclude(Lookups.metaInfServices(l), AlwaysRelativeCollocationQuery.class));
161
        }));
162
        scratch = TestUtil.makeScratchDir(this);
161
        scratch = TestUtil.makeScratchDir(this);
163
        projdir = scratch.createFolder("proj");
162
        projdir = scratch.createFolder("proj");
164
        TestUtil.createFileFromContent(ReferenceHelperTest.class.getResource("data/project.xml"), projdir, "nbproject/project.xml");
163
        TestUtil.createFileFromContent(ReferenceHelperTest.class.getResource("data/project.xml"), projdir, "nbproject/project.xml");
Lines 219-225 Link Here
219
        p = null;
218
        p = null;
220
        h = null;
219
        h = null;
221
        //l = null;
220
        //l = null;
222
        TestUtil.setLookup(Lookup.EMPTY);
223
        super.tearDown();
221
        super.tearDown();
224
    }
222
    }
225
223
Lines 1093-1098 Link Here
1093
            actualScriptLocations.add(Util.findText(script));
1091
            actualScriptLocations.add(Util.findText(script));
1094
        }
1092
        }
1095
        assertEquals(Arrays.asList(scriptLocations), actualScriptLocations);
1093
        assertEquals(Arrays.asList(scriptLocations), actualScriptLocations);
1094
    }
1095
1096
    public void testProjectLibraryReferences() throws Exception {
1097
        ProjectLibraryProvider.FIRE_CHANGES_SYNCH = true;
1098
        assertProjectLibraryManagers(null);
1099
        File fooJar = new File(getWorkDir(), "foo.jar");
1100
        File f = new File(getWorkDir(), "libs.properties");
1101
        URL loc = f.toURI().toURL();
1102
        LibraryManager mgr = LibraryManager.forLocation(loc);
1103
        assertEquals(loc, mgr.getLocation());
1104
        Library fooLib = mgr.createLibrary("j2se", "foo",
1105
                Collections.singletonMap("classpath", Arrays.asList(new URL("jar:" + fooJar.toURI() + "!/"))));
1106
        assertEquals(mgr, fooLib.getManager());
1107
        try {
1108
            r.createLibraryReference(fooLib, "classpath");
1109
            fail("cannot reference library which is not reachable from project");
1110
        } catch (IllegalArgumentException ex) {
1111
            // as expected
1112
        }
1113
        h.setLibrariesLocation(f.getAbsolutePath());
1114
        String fooref = r.createLibraryReference(fooLib, "classpath");
1115
        assertEquals("${libs.foo.classpath}", fooref);
1116
        assertEquals(fooJar.getAbsolutePath(), pev.evaluate(fooref).replace('/', File.separatorChar));
1117
        assertProjectLibraryManagers(loc);
1118
        assertEquals("foo", r.findLibrary("foo").getName());
1119
        assertEquals("foo", r.findLibrary("${libs.foo.classpath}").getName());
1120
        assertNull(r.findLibrary("nonexistent"));
1121
        assertNull(r.findLibrary("${libs.nonexistent.classpath}"));
1122
        assertNull(r.findLibrary("${some.other.foo.stuff}"));
1123
        File barDir = new File(getWorkDir(), "bar");
1124
        barDir.mkdirs();
1125
        String barref = r.createLibraryReference(mgr.createLibrary("j2se", "bar",
1126
                Collections.singletonMap("classpath", Arrays.asList(barDir.toURI().toURL()))), "classpath");
1127
        assertEquals("${libs.bar.classpath}", barref);
1128
        assertEquals(barDir.getAbsolutePath(), pev.evaluate(barref).replace('/', File.separatorChar));
1129
        EditableProperties props = h.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
1130
        props.put("javac.classpath", "stuff:" + fooref + ":which-is-not-bar");
1131
        h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props);
1132
        assertProjectLibraryManagers(loc);
1133
        assertEquals("foo", r.findLibrary("foo").getName());
1134
        assertEquals("foo", r.findLibrary("${libs.foo.classpath}").getName());
1135
        assertEquals("bar", r.findLibrary("bar").getName());
1136
        assertEquals("bar", r.findLibrary("${libs.bar.classpath}").getName());
1137
        mgr.createLibrary("j2se", "empty", Collections.<String,List<URL>>emptyMap());
1138
        assertEquals("stuff:" + fooJar + ":which-is-not-bar", pev.evaluate("${javac.classpath}").replace('/', File.separatorChar));
1139
        props.put("javac.classpath", "nolibshere");
1140
        h.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props);
1141
        assertEquals("nolibshere", pev.getProperty("javac.classpath"));
1142
        assertEquals(fooJar.getAbsolutePath(), pev.getProperty("libs.foo.classpath").replace('/', File.separatorChar));
1143
        assertProjectLibraryManagers(loc);
1144
        assertEquals("foo", r.findLibrary("foo").getName());
1145
        assertEquals("foo", r.findLibrary("${libs.foo.classpath}").getName());
1146
        assertEquals("bar", r.findLibrary("bar").getName());
1147
        assertEquals("bar", r.findLibrary("${libs.bar.classpath}").getName());
1148
    }
1149
    private void assertProjectLibraryManagers(URL expectedUrl) {
1150
        LibraryManager mgr = r.getProjectLibraryManager();
1151
        if (mgr == null) {
1152
            assertNull(expectedUrl);
1153
        } else {
1154
            assertEquals(expectedUrl, mgr.getLocation());
1155
        }
1096
    }
1156
    }
1097
1157
1098
}
1158
}

Return to bug 44035