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

(-)a/apisupport.project/nbproject/project.properties (-1 / +1 lines)
Lines 39-45 Link Here
39
39
40
javac.compilerargs=-Xlint -Xlint:-serial
40
javac.compilerargs=-Xlint -Xlint:-serial
41
javac.source=1.5
41
javac.source=1.5
42
spec.version.base=1.20.0
42
spec.version.base=1.21.0
43
43
44
javadoc.arch=${basedir}/arch.xml
44
javadoc.arch=${basedir}/arch.xml
45
javadoc.apichanges=${basedir}/apichanges.xml
45
javadoc.apichanges=${basedir}/apichanges.xml
(-)a/apisupport.project/nbproject/project.xml (+1 lines)
Lines 185-190 Link Here
185
                    <compile-dependency/>
185
                    <compile-dependency/>
186
                    <run-dependency>
186
                    <run-dependency>
187
                        <release-version>1</release-version>
187
                        <release-version>1</release-version>
188
                        <specification-version>1.21</specification-version>
188
                    </run-dependency>
189
                    </run-dependency>
189
                </dependency>
190
                </dependency>
190
                <dependency>
191
                <dependency>
(-)a/apisupport.project/src/org/netbeans/modules/apisupport/project/NbModuleProject.java (+1 lines)
Lines 232-237 Link Here
232
            new Info(),
232
            new Info(),
233
            aux,
233
            aux,
234
            helper.createCacheDirectoryProvider(),
234
            helper.createCacheDirectoryProvider(),
235
            helper.createAuxiliaryProperties(),
235
            new SavedHook(),
236
            new SavedHook(),
236
            UILookupMergerSupport.createProjectOpenHookMerger(new OpenedHook()),
237
            UILookupMergerSupport.createProjectOpenHookMerger(new OpenedHook()),
237
            new ModuleActions(this),
238
            new ModuleActions(this),
(-)a/java.j2seproject/nbproject/project.properties (-1 / +1 lines)
Lines 39-45 Link Here
39
39
40
javac.compilerargs=-Xlint -Xlint:-serial
40
javac.compilerargs=-Xlint -Xlint:-serial
41
javac.source=1.5
41
javac.source=1.5
42
spec.version.base=1.14.0
42
spec.version.base=1.15.0
43
43
44
javadoc.arch=${basedir}/arch.xml
44
javadoc.arch=${basedir}/arch.xml
45
javadoc.apichanges=${basedir}/apichanges.xml
45
javadoc.apichanges=${basedir}/apichanges.xml
(-)a/java.j2seproject/nbproject/project.xml (-1 / +1 lines)
Lines 180-186 Link Here
180
                    <compile-dependency/>
180
                    <compile-dependency/>
181
                    <run-dependency>
181
                    <run-dependency>
182
                        <release-version>1</release-version>
182
                        <release-version>1</release-version>
183
                        <specification-version>1.17</specification-version>
183
                        <specification-version>1.21</specification-version>
184
                    </run-dependency>
184
                    </run-dependency>
185
                </dependency>
185
                </dependency>
186
                <dependency>
186
                <dependency>
(-)a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java (+1 lines)
Lines 260-265 Link Here
260
            new Info(),
260
            new Info(),
261
            aux,
261
            aux,
262
            helper.createCacheDirectoryProvider(),
262
            helper.createCacheDirectoryProvider(),
263
            helper.createAuxiliaryProperties(),
263
            spp,
264
            spp,
264
            actionProvider,
265
            actionProvider,
265
            new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), spp, refHelper),
266
            new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), spp, refHelper),
(-)a/project.ant/apichanges.xml (+16 lines)
Lines 105-110 Link Here
105
105
106
    <changes>
106
    <changes>
107
107
108
        <change id="AntProjectHelper-createAuxiliaryProperties">
109
            <api name="general"/>
110
            <summary>Added AntProjectHelper.createAuxiliaryProperties()</summary>
111
            <version major="1" minor="21"/>
112
            <date day="9" month="6" year="2008"/>
113
            <author login="jlahoda"/>
114
            <compatibility addition="yes"/>
115
            <description>
116
                <p>
117
                    A new API method <code>AntProjectHelper.createAuxiliaryProperties</code>.
118
                </p>
119
            </description>
120
            <class package="org.netbeans.spi.project.support.ant" name="AntProjectHelper"/>
121
            <issue number="134580"/>
122
        </change>
123
        
108
        <change id="ant-project-libraries">
124
        <change id="ant-project-libraries">
109
            <api name="general"/>
125
            <api name="general"/>
110
            <summary>Support for project-specific libraries</summary>
126
            <summary>Support for project-specific libraries</summary>
(-)a/project.ant/manifest.mf (-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.20
3
OpenIDE-Module-Specification-Version: 1.21
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
(-)a/project.ant/nbproject/project.xml (-1 / +1 lines)
Lines 78-84 Link Here
78
                    <compile-dependency/>
78
                    <compile-dependency/>
79
                    <run-dependency>
79
                    <run-dependency>
80
                        <release-version>1</release-version>
80
                        <release-version>1</release-version>
81
                        <specification-version>1.6</specification-version>
81
                        <specification-version>1.16</specification-version>
82
                    </run-dependency>
82
                    </run-dependency>
83
                </dependency>
83
                </dependency>
84
                <dependency>
84
                <dependency>
(-)a/project.ant/src/org/netbeans/spi/project/support/ant/AntProjectHelper.java (+16 lines)
Lines 64-69 Link Here
64
import org.netbeans.modules.project.ant.UserQuestionHandler;
64
import org.netbeans.modules.project.ant.UserQuestionHandler;
65
import org.netbeans.modules.project.ant.Util;
65
import org.netbeans.modules.project.ant.Util;
66
import org.netbeans.spi.project.AuxiliaryConfiguration;
66
import org.netbeans.spi.project.AuxiliaryConfiguration;
67
import org.netbeans.spi.project.AuxiliaryProperties;
67
import org.netbeans.spi.project.CacheDirectoryProvider;
68
import org.netbeans.spi.project.CacheDirectoryProvider;
68
import org.netbeans.spi.project.ProjectState;
69
import org.netbeans.spi.project.ProjectState;
69
import org.netbeans.spi.queries.FileBuiltQueryImplementation;
70
import org.netbeans.spi.queries.FileBuiltQueryImplementation;
Lines 930-935 Link Here
930
    }
931
    }
931
    
932
    
932
    /**
933
    /**
934
     * Create an object permitting this project to expose {@link AuxiliaryProperties}.
935
     * Would be placed into the project's lookup.
936
     * 
937
     * This implementation places the properties into {@link #PROJECT_PROPERTIES_PATH}
938
     * or {@link #PRIVATE_PROPERTIES_PATH} (depending on shared value). The properties are
939
     * prefixed with "<code>auxiliary.</code>".
940
     * 
941
     * @return an instance of {@link AuxiliaryProperties} suitable for the project lookup
942
     * @since 1.21
943
     */
944
    public AuxiliaryProperties createAuxiliaryProperties() {
945
        return new AuxiliaryPropertiesImpl(this);
946
    }
947
    
948
    /**
933
     * Create an implementation of {@link org.netbeans.api.queries.FileBuiltQuery} that works with files
949
     * Create an implementation of {@link org.netbeans.api.queries.FileBuiltQuery} that works with files
934
     * within the project based on simple glob pattern mappings.
950
     * within the project based on simple glob pattern mappings.
935
     * <p>
951
     * <p>
(-)a/project.ant/src/org/netbeans/spi/project/support/ant/AuxiliaryPropertiesImpl.java (+100 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
4
 * Copyright 2008 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 2008 Sun Microsystems, Inc.
38
 */
39
40
package org.netbeans.spi.project.support.ant;
41
import java.util.LinkedList;
42
import java.util.List;
43
import org.netbeans.api.project.ProjectManager;
44
import org.netbeans.spi.project.AuxiliaryProperties;
45
import org.openide.util.Mutex;
46
47
/**
48
 *
49
 * @author Jan Lahoda
50
 */
51
class AuxiliaryPropertiesImpl implements AuxiliaryProperties {
52
53
    private final AntProjectHelper helper;
54
    private final String propertyPrefix = "auxiliary.";
55
56
    public AuxiliaryPropertiesImpl(AntProjectHelper helper) {
57
        this.helper = helper;
58
    }
59
    
60
    public String get(String key, boolean shared) {
61
        String location = shared ? AntProjectHelper.PROJECT_PROPERTIES_PATH : AntProjectHelper.PRIVATE_PROPERTIES_PATH;
62
        EditableProperties props = helper.getProperties(location);
63
        
64
        return props.get(propertyPrefix + key);
65
    }
66
67
    public void put(final String key, final String value, final boolean shared) {
68
        ProjectManager.mutex().writeAccess(new Mutex.Action<Void>() {
69
            public Void run() {
70
                String location = shared ? AntProjectHelper.PROJECT_PROPERTIES_PATH : AntProjectHelper.PRIVATE_PROPERTIES_PATH;
71
                EditableProperties props = helper.getProperties(location);
72
73
                if (value != null) {
74
                    props.put(propertyPrefix + key, value);
75
                } else {
76
                    props.remove(propertyPrefix + key);
77
                }
78
79
                helper.putProperties(location, props);
80
                
81
                return null;
82
            }
83
        });
84
    }
85
86
    public Iterable<String> listKeys(boolean shared) {
87
        List<String> result = new LinkedList<String>();
88
        String location = shared ? AntProjectHelper.PROJECT_PROPERTIES_PATH : AntProjectHelper.PRIVATE_PROPERTIES_PATH;
89
        EditableProperties props = helper.getProperties(location);
90
        
91
        for (String k : props.keySet()) {
92
            if (k.startsWith(propertyPrefix)) {
93
                result.add(k.substring(propertyPrefix.length()));
94
            }
95
        }
96
        
97
        return result;
98
    }
99
100
}
(-)a/projectapi/apichanges.xml (+18 lines)
Lines 104-109 Link Here
104
    <!-- ACTUAL CHANGES BEGIN HERE: -->
104
    <!-- ACTUAL CHANGES BEGIN HERE: -->
105
105
106
    <changes>
106
    <changes>
107
        <change id="projectutils-getPreferences">
108
            <api name="general"/>
109
            <summary>Added ProjectUtils.getPreferences.</summary>
110
            <version major="1" minor="16"/>
111
            <date day="6" month="6" year="2008"/>
112
            <author login="jlahoda"/>
113
            <compatibility addition="yes" binary="compatible" deletion="no" deprecation="no" modification="no" semantic="compatible" source="compatible">
114
            </compatibility>
115
            <description>
116
	        <p>
117
                    Added new API method <code>ProjectUtils.getPreferences</code> and new SPI interface <code>AuxiliaryProperties</code>.
118
		</p>
119
            </description>
120
            <class package="org.netbeans.api.project" name="ProjectUtils"/>
121
            <class package="org.netbeans.spi.project" name="AuxiliaryProperties"/>
122
            <issue number="134580"/>
123
        </change>
124
        
107
        <change id="lookup-provider">
125
        <change id="lookup-provider">
108
            <api name="general"/>
126
            <api name="general"/>
109
            <summary>Added support for composing project's lookup from multiple sources.</summary>
127
            <summary>Added support for composing project's lookup from multiple sources.</summary>
(-)a/projectapi/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.modules.projectapi/1
2
OpenIDE-Module: org.netbeans.modules.projectapi/1
3
OpenIDE-Module-Specification-Version: 1.15
3
OpenIDE-Module-Specification-Version: 1.16
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/projectapi/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/projectapi/Bundle.properties
5
5
(-)a/projectapi/nbproject/project.xml (-8 / +16 lines)
Lines 72-77 Link Here
72
                    </run-dependency>
72
                    </run-dependency>
73
                </dependency>
73
                </dependency>
74
                <dependency>
74
                <dependency>
75
                    <code-name-base>org.openide.modules</code-name-base>
76
                    <build-prerequisite/>
77
                    <compile-dependency/>
78
                    <run-dependency>
79
                        <specification-version>7.7</specification-version>
80
                    </run-dependency>
81
                </dependency>
82
                <dependency>
75
                    <code-name-base>org.openide.nodes</code-name-base>
83
                    <code-name-base>org.openide.nodes</code-name-base>
76
                    <build-prerequisite/>
84
                    <build-prerequisite/>
77
                    <compile-dependency/>
85
                    <compile-dependency/>
Lines 92-114 Link Here
92
                <test-type>
100
                <test-type>
93
                    <name>unit</name>
101
                    <name>unit</name>
94
                    <test-dependency>
102
                    <test-dependency>
103
                        <code-name-base>org.netbeans.modules.masterfs</code-name-base>
104
                    </test-dependency>
105
                    <test-dependency>
95
                        <code-name-base>org.netbeans.modules.projectapi</code-name-base>
106
                        <code-name-base>org.netbeans.modules.projectapi</code-name-base>
96
                        <recursive/>
107
                        <recursive/>
97
                        <compile-dependency/>
108
                        <compile-dependency/>
98
                    </test-dependency>
109
                    </test-dependency>
99
                    <test-dependency>
110
                    <test-dependency>
100
                        <code-name-base>org.openide.modules</code-name-base>
111
                        <code-name-base>org.openide.filesystems</code-name-base>
101
                    </test-dependency>
102
                    <test-dependency>
103
                        <code-name-base>org.netbeans.modules.masterfs</code-name-base>
104
                    </test-dependency>
105
                    <test-dependency>
106
                        <code-name-base>org.openide.util</code-name-base>
107
                        <compile-dependency/>
112
                        <compile-dependency/>
108
                        <test/>
113
                        <test/>
109
                    </test-dependency>
114
                    </test-dependency>
110
                    <test-dependency>
115
                    <test-dependency>
111
                        <code-name-base>org.openide.filesystems</code-name-base>
116
                        <code-name-base>org.openide.modules</code-name-base>
117
                    </test-dependency>
118
                    <test-dependency>
119
                        <code-name-base>org.openide.util</code-name-base>
112
                        <compile-dependency/>
120
                        <compile-dependency/>
113
                        <test/>
121
                        <test/>
114
                    </test-dependency>
122
                    </test-dependency>
(-)a/projectapi/src/org/netbeans/api/project/ProjectUtils.java (+24 lines)
Lines 47-58 Link Here
47
import java.util.HashSet;
47
import java.util.HashSet;
48
import java.util.Map;
48
import java.util.Map;
49
import java.util.Set;
49
import java.util.Set;
50
import java.util.prefs.Preferences;
50
import javax.swing.Icon;
51
import javax.swing.Icon;
51
import javax.swing.ImageIcon;
52
import javax.swing.ImageIcon;
53
import org.netbeans.modules.projectapi.AuxiliaryConfigBasedPreferencesProvider;
52
import org.netbeans.spi.project.SubprojectProvider;
54
import org.netbeans.spi.project.SubprojectProvider;
53
import org.netbeans.spi.project.support.GenericSources;
55
import org.netbeans.spi.project.support.GenericSources;
54
import org.openide.filesystems.FileStateInvalidException;
56
import org.openide.filesystems.FileStateInvalidException;
55
import org.openide.util.Mutex;
57
import org.openide.util.Mutex;
58
import org.openide.util.Parameters;
56
import org.openide.util.Utilities;
59
import org.openide.util.Utilities;
57
60
58
/**
61
/**
Lines 143-148 Link Here
143
    }
146
    }
144
    
147
    
145
    /**
148
    /**
149
     * Return {@link Preferences} for the given project and given module.
150
     * 
151
     * <p class="nonnormative">
152
     * The preferences are stored in the project using either {@link AuxiliaryConfiguration}
153
     * or {@link AuxiliaryProperties}.
154
     * </p>
155
     * 
156
     * @param project project for which preferences should be returned
157
     * @param clazz module specification as in {@link org.openide.util.NbPreferences#forModule(java.lang.Class)}
158
     * @param shared whether the returned settings should be shared
159
     * @return {@link Preferences} for the given project
160
     * @since 1.16
161
     */
162
    public static Preferences getPreferences(Project project, Class clazz, boolean shared) {
163
        Parameters.notNull("project", project);
164
        Parameters.notNull("clazz", clazz);
165
        
166
        return AuxiliaryConfigBasedPreferencesProvider.getPreferences(project, clazz, shared);
167
    }
168
    
169
    /**
146
     * Do a DFS traversal checking for cycles.
170
     * Do a DFS traversal checking for cycles.
147
     * @param encountered projects already encountered in the DFS (added and removed as you go)
171
     * @param encountered projects already encountered in the DFS (added and removed as you go)
148
     * @param curr current node to visit
172
     * @param curr current node to visit
(-)a/projectapi/src/org/netbeans/modules/projectapi/AuxiliaryConfigBasedPreferencesProvider.java (+674 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2008 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 2008 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.projectapi;
43
44
import java.io.IOException;
45
import java.lang.ref.Reference;
46
import java.lang.ref.WeakReference;
47
import java.util.Collection;
48
import java.util.Collections;
49
import java.util.Enumeration;
50
import java.util.HashMap;
51
import java.util.HashSet;
52
import java.util.LinkedHashSet;
53
import java.util.LinkedList;
54
import java.util.List;
55
import java.util.Map;
56
import java.util.Map.Entry;
57
import java.util.Set;
58
import java.util.WeakHashMap;
59
import java.util.prefs.AbstractPreferences;
60
import java.util.prefs.BackingStoreException;
61
import java.util.prefs.Preferences;
62
import org.netbeans.api.project.Project;
63
import org.netbeans.api.project.ProjectManager;
64
import org.netbeans.spi.project.AuxiliaryConfiguration;
65
import org.netbeans.spi.project.AuxiliaryProperties;
66
import org.openide.filesystems.FileObject;
67
import org.openide.modules.ModuleInfo;
68
import org.openide.util.Exceptions;
69
import org.openide.util.Lookup;
70
import org.openide.util.RequestProcessor;
71
import org.openide.util.RequestProcessor.Task;
72
import org.openide.xml.XMLUtil;
73
import org.w3c.dom.DOMException;
74
import org.w3c.dom.Element;
75
import org.w3c.dom.Node;
76
import org.w3c.dom.NodeList;
77
78
/**
79
 * @author Jan Lahoda
80
 */
81
public class AuxiliaryConfigBasedPreferencesProvider {
82
    
83
    private static Map<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>> projects2SharedPrefs = new WeakHashMap<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>>();
84
    private static Map<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>> projects2PrivatePrefs = new WeakHashMap<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>>();
85
    
86
    static synchronized AuxiliaryConfigBasedPreferencesProvider findProvider(Project p, boolean shared) {
87
        Map<Project, Reference<AuxiliaryConfigBasedPreferencesProvider>> target = shared ? projects2SharedPrefs : projects2PrivatePrefs;
88
        Reference<AuxiliaryConfigBasedPreferencesProvider> provRef = target.get(p);
89
        AuxiliaryConfigBasedPreferencesProvider prov = provRef != null ? provRef.get() : null;
90
        
91
        if (prov != null) {
92
            return prov;
93
        }
94
        
95
        AuxiliaryConfiguration ac = p.getLookup().lookup(AuxiliaryConfiguration.class);
96
        AuxiliaryProperties ap = p.getLookup().lookup(AuxiliaryProperties.class);
97
        
98
        if (ac != null || ap != null) {
99
            target.put(p, new WeakReference<AuxiliaryConfigBasedPreferencesProvider>(prov = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, shared)));
100
        } else {
101
            ap = new FallbackAuxiliaryPropertiesImpl(p.getProjectDirectory());
102
            target.put(p, new WeakReference<AuxiliaryConfigBasedPreferencesProvider>(prov = new AuxiliaryConfigBasedPreferencesProvider(p, null, ap, shared)));
103
        }
104
        
105
        return prov;
106
    }
107
    
108
    public static Preferences getPreferences(Project project, Class clazz, boolean shared) {
109
        AuxiliaryConfigBasedPreferencesProvider provider = findProvider(project, shared);
110
111
        if (provider == null) {
112
            return null;
113
        }
114
        
115
        return provider.findModule(AuxiliaryConfigBasedPreferencesProvider.findCNBForClass(clazz));
116
    }
117
118
    private static String encodeString(String s) {
119
        StringBuilder result = new StringBuilder();
120
        
121
        for (char c : s.toCharArray()) {
122
            if (INVALID_KEY_CHARACTERS.indexOf(c) == (-1)) {
123
                result.append(c);
124
            } else {
125
                result.append("_");
126
                result.append(Integer.toHexString((int) c));
127
                result.append("_");
128
            }
129
        }
130
        
131
        return result.toString();
132
    }
133
    
134
    private static String decodeString(String s) {
135
        StringBuilder result = new StringBuilder();
136
        String[]      parts  = s.split("_");
137
        
138
        for (int cntr = 0; cntr < parts.length; cntr += 2) {
139
            result.append(parts[cntr]);
140
                
141
            if (cntr + 1 < parts.length) {
142
                result.append((char) Integer.parseInt(parts[cntr + 1], 16));
143
            }
144
        }
145
        
146
        return result.toString();
147
    }
148
    
149
            static final String NAMESPACE = "http://www.netbeans.org/ns/auxiliary-configuration-preferences/1";
150
151
            static final String EL_PREFERENCES = "preferences";
152
    private static final String EL_MODULE = "module";
153
    private static final String EL_PROPERTY = "property";
154
    private static final String EL_NODE = "node";
155
    
156
    private static final String ATTR_NAME = "name";
157
    private static final String ATTR_VALUE = "value";
158
    
159
    private static final String INVALID_KEY_CHARACTERS = "_.";
160
    
161
    private static final RequestProcessor WORKER = new RequestProcessor("AuxiliaryConfigBasedPreferencesProvider worker", 1);
162
    private static final int AUTOFLUSH_TIMEOUT = 5000;
163
164
    private final Project project;
165
    private final AuxiliaryConfiguration ac;
166
    private final AuxiliaryProperties ap;
167
    private final boolean shared;
168
    private final Map<String, Reference<AuxiliaryConfigBasedPreferences>> module2Preferences = new HashMap<String, Reference<AuxiliaryConfigBasedPreferences>>();
169
    private Element configRoot;
170
    private boolean modified;
171
    private final Task autoFlushTask = WORKER.create(new Runnable() {
172
        public void run() {
173
            flush();
174
        }
175
    });
176
    
177
    private final Map<String, Map<String, String>> path2Data = new HashMap<String, Map<String, String>>();
178
    private final Map<String, Set<String>> path2Removed = new HashMap<String, Set<String>>();
179
    private final Set<String> removedNodes = new HashSet<String>();
180
    private final Set<String> createdNodes = new HashSet<String>();
181
182
    AuxiliaryConfigBasedPreferencesProvider(Project project, AuxiliaryConfiguration ac, AuxiliaryProperties ap, boolean shared) {
183
        this.project = project;
184
        this.ac = ac;
185
        this.ap = ap;
186
        this.shared = shared;
187
        loadConfigRoot();
188
    }
189
    
190
    private void loadConfigRoot() {
191
        if (ac == null) {
192
            return ;
193
        }
194
        
195
        Element configRootLoc = ac.getConfigurationFragment(EL_PREFERENCES, NAMESPACE, shared);
196
197
        if (configRootLoc == null) {
198
            configRootLoc = XMLUtil.createDocument(EL_PREFERENCES, NAMESPACE, null, null).createElementNS(NAMESPACE,
199
                    EL_PREFERENCES);
200
        }
201
202
        this.configRoot = configRootLoc;
203
    }
204
    
205
    synchronized void flush() {
206
        if (!modified) {
207
            return ;
208
        }
209
        
210
        boolean domModified = false;
211
        
212
        for (String removedNode : removedNodes) {
213
            if (ac != null) {
214
                Element el = findRelative(removedNode, false);
215
216
                if (el != null) {
217
                    el.getParentNode().removeChild(el);
218
219
                    domModified = true;
220
                }
221
            }
222
            
223
            if (ap != null) {
224
                String propName = toPropertyName(removedNode, "");
225
                
226
                for (String key : ap.listKeys(shared)) {
227
                    if (key.startsWith(propName)) {
228
                        ap.put(key, null, shared);
229
                    }
230
                }
231
            }
232
        }
233
        
234
        for (String createdNode : createdNodes) {
235
            if (ap != null) {
236
                String propName = toPropertyName(createdNode, "");
237
                
238
                ap.put(propName, "", shared);
239
            } else {
240
                findRelative(createdNode, true);
241
                
242
                domModified = true;
243
            }
244
        }
245
        
246
        for (Entry<String, Map<String, String>> e : path2Data.entrySet()) {
247
            if (ap != null) {
248
                for (Entry<String, String> value : e.getValue().entrySet()) {
249
                    ap.put(toPropertyName(e.getKey(), value.getKey()), value.getValue(), shared);
250
                }
251
            } else {
252
                Element el = findRelative(e.getKey(), true);
253
254
                if (el != null) {
255
                    for (Entry<String, String> value : e.getValue().entrySet()) {
256
                        Element p = find(el, value.getKey(), EL_PROPERTY, true);
257
258
                        p.setAttribute(ATTR_VALUE, value.getValue());
259
                    }
260
261
                    domModified = true;
262
                }
263
            }
264
        }
265
        
266
        for (Entry<String, Set<String>> e : path2Removed.entrySet()) {
267
            if (ac != null) {
268
                Element el = findRelative(e.getKey(), false);
269
270
                if (el != null) {
271
                    for (String removed : e.getValue()) {
272
                        Element p = find(el, removed, EL_PROPERTY, true);
273
274
                        el.removeChild(p);
275
                    }
276
277
                    domModified = true;
278
                }
279
            }
280
            
281
            if (ap != null) {
282
                for (String removed : e.getValue()) {
283
                    ap.put(toPropertyName(e.getKey(), removed), null, shared);
284
                }
285
            }
286
        }
287
        
288
        if (domModified) {
289
            ac.putConfigurationFragment(configRoot, true);
290
        }
291
        
292
        try {
293
            ProjectManager.getDefault().saveProject(project);
294
        } catch (IOException ex) {
295
            Exceptions.printStackTrace(ex);
296
        }
297
        
298
        path2Data.clear();
299
        path2Removed.clear();
300
        removedNodes.clear();
301
        createdNodes.clear();
302
        modified = false;
303
    }
304
    
305
    synchronized void sync() {
306
        loadConfigRoot();
307
        flush();
308
    }
309
    
310
    private void markModified() {
311
        autoFlushTask.cancel();
312
        autoFlushTask.schedule(AUTOFLUSH_TIMEOUT);
313
        modified = true;
314
    }
315
    
316
    private static String findCNBForClass(Class cls) {
317
        String absolutePath = null;
318
        ClassLoader cl = cls.getClassLoader();
319
        for (ModuleInfo module : Lookup.getDefault().lookupAll(ModuleInfo.class)) {
320
            if (module.isEnabled() && module.getClassLoader() == cl) {
321
                absolutePath = module.getCodeNameBase();
322
                break;
323
            }
324
        }
325
        if (absolutePath == null) {
326
            absolutePath = cls.getName().replaceFirst("(^|\\.)[^.]+$", "");//NOI18N
327
        }
328
        assert absolutePath != null;
329
        return absolutePath.replace('.', '-');
330
    }
331
    
332
    public synchronized Preferences findModule(String moduleName) {
333
        Reference<AuxiliaryConfigBasedPreferences> prefRef = module2Preferences.get(moduleName);
334
        AuxiliaryConfigBasedPreferences pref = prefRef != null ? prefRef.get() : null;
335
        
336
        if (pref == null) {
337
            module2Preferences.put(moduleName, new WeakReference<AuxiliaryConfigBasedPreferences>(pref = new AuxiliaryConfigBasedPreferences(null, "", moduleName)));
338
        }
339
        
340
        return pref;
341
    }
342
    
343
    private Element findRelative(String path, boolean createIfMissing) {
344
        if (ac == null) {
345
            return null;
346
        }
347
        
348
        String[] sep = path.split("/");
349
        
350
        assert sep.length > 0;
351
        
352
        Element e = find(configRoot, sep[0], EL_MODULE, createIfMissing);
353
        
354
        for (int cntr = 1; cntr < sep.length && e != null; cntr++) {
355
            e = find(e, sep[cntr], EL_NODE, createIfMissing);
356
        }
357
        
358
        return e;
359
    }
360
361
    private Map<String, String> getData(String path) {
362
        Map<String, String> data = path2Data.get(path);
363
        
364
        if (data == null) {
365
            path2Data.put(path, data = new HashMap<String, String>());
366
        }
367
        
368
        return data;
369
    }
370
    
371
    private Set<String> getRemoved(String path) {
372
        Set<String> removed = path2Removed.get(path);
373
        
374
        if (removed == null) {
375
            path2Removed.put(path, removed = new HashSet<String>());
376
        }
377
        
378
        return removed;
379
    }
380
    
381
    private void removeNode(String path) {
382
        path2Data.remove(path);
383
        path2Removed.remove(path);
384
        createdNodes.remove(path);
385
        removedNodes.add(path);
386
    }
387
    
388
    private boolean isRemovedNode(String path) {
389
        return removedNodes.contains(path);
390
    }
391
    
392
    private static Element find(Element dom, String key, String elementName, boolean createIfMissing) {
393
        NodeList nl = dom.getChildNodes();
394
        
395
        for (int cntr = 0; cntr < nl.getLength(); cntr++) {
396
            Node n = nl.item(cntr);
397
            
398
            if (n.getNodeType() == Node.ELEMENT_NODE && NAMESPACE.equals(n.getNamespaceURI()) && elementName.equals(n.getLocalName())) {
399
                if (key.equals(((Element) n).getAttribute(ATTR_NAME))) {
400
                    return (Element) n;
401
                }
402
            }
403
        }
404
        
405
        if (!createIfMissing) {
406
            return null;
407
        }
408
        
409
        Element el = dom.getOwnerDocument().createElementNS(NAMESPACE, elementName);
410
        
411
        el.setAttribute(ATTR_NAME, key);
412
        
413
        dom.appendChild(el);
414
        
415
        return el;
416
    }
417
    
418
    private String toPropertyName(String path, String propertyName) {
419
        return encodeString(path).replace('/', '.') + '.' + encodeString(propertyName);
420
    }
421
422
    private class AuxiliaryConfigBasedPreferences extends AbstractPreferences {
423
424
        private final String path;
425
        
426
        public AuxiliaryConfigBasedPreferences(AbstractPreferences parent, String name, String path) {
427
            super(parent, name);
428
            this.path = path;
429
        }
430
431
        @Override
432
        protected void putSpi(String key, String value) {
433
            synchronized (AuxiliaryConfigBasedPreferencesProvider.this) {
434
                getData(path).put(key, value);
435
                getRemoved(path).remove(key);
436
437
                markModified();
438
            }
439
        }
440
441
        @Override
442
        protected String getSpi(String key) {
443
            synchronized (AuxiliaryConfigBasedPreferencesProvider.this) {
444
                if (getRemoved(path).contains(key)) {
445
                    return null;
446
                }
447
448
                if (getData(path).containsKey(key)) {
449
                    return getData(path).get(key);
450
                }
451
452
                if (isRemovedNode(path)) {
453
                    return null;
454
                }
455
456
                if (ap != null ) {
457
                    String keyProp = toPropertyName(path, key);
458
                    String res = AuxiliaryConfigBasedPreferencesProvider.this.ap.get(keyProp, shared);
459
                    
460
                    if (res != null) {
461
                        return res;
462
                    }
463
                }
464
                Element p = findRelative(path, false);
465
466
                p = p != null ? AuxiliaryConfigBasedPreferencesProvider.find(p, key, EL_PROPERTY, false) : null;
467
468
                if (p == null) {
469
                    return null;
470
                }
471
472
                return p.getAttribute(ATTR_VALUE);
473
            }
474
        }
475
476
        @Override
477
        protected void removeSpi(String key) {
478
            synchronized (AuxiliaryConfigBasedPreferencesProvider.this) {
479
                getData(path).remove(key);
480
                getRemoved(path).add(key);
481
482
                markModified();
483
            }
484
        }
485
486
        @Override
487
        protected void removeNodeSpi() throws BackingStoreException {
488
            synchronized (AuxiliaryConfigBasedPreferencesProvider.this) {
489
                AuxiliaryConfigBasedPreferencesProvider.this.removeNode(path);
490
                markModified();
491
            }
492
        }
493
494
        @Override
495
        protected String[] keysSpi() throws BackingStoreException {
496
            synchronized (AuxiliaryConfigBasedPreferencesProvider.this) {
497
                Collection<String> result = new LinkedHashSet<String>();
498
499
                if (!isRemovedNode(path)) {
500
                    result.addAll(list(EL_PROPERTY));
501
                }
502
                
503
                if (ap != null) {
504
                    String prefix = toPropertyName(path, "");
505
                    
506
                    for (String key : ap.listKeys(shared)) {
507
                        if (key.startsWith(prefix)) {
508
                            String name = key.substring(prefix.length());
509
                            
510
                            if (name.length() > 0 && name.indexOf('.') == (-1)) {
511
                                result.add(decodeString(name));
512
                            }
513
                        }
514
                    }
515
                }
516
517
                result.addAll(getData(path).keySet());
518
                result.removeAll(getRemoved(path));
519
520
                return result.toArray(new String[0]);
521
            }
522
        }
523
524
        @Override
525
        protected String[] childrenNamesSpi() throws BackingStoreException {
526
            synchronized (AuxiliaryConfigBasedPreferencesProvider.this) {
527
                return getChildrenNames().toArray(new String[0]);
528
            }
529
        }
530
531
        @Override
532
        protected AbstractPreferences childSpi(String name) {
533
            synchronized (AuxiliaryConfigBasedPreferencesProvider.this) {
534
                String nuePath = path + "/" + name;
535
                if (!getChildrenNames().contains(name)) {
536
                    AuxiliaryConfigBasedPreferencesProvider.this.createdNodes.add(nuePath);
537
                }
538
539
                return new AuxiliaryConfigBasedPreferences(this, name, nuePath);
540
            }
541
        }
542
543
        @Override
544
        public void sync() throws BackingStoreException {
545
            AuxiliaryConfigBasedPreferencesProvider.this.sync();
546
        }
547
548
        @Override
549
        protected void syncSpi() throws BackingStoreException {
550
            throw new UnsupportedOperationException("Should never be called.");
551
        }
552
553
        @Override
554
        public void flush() throws BackingStoreException {
555
            AuxiliaryConfigBasedPreferencesProvider.this.flush();
556
        }
557
558
        @Override
559
        protected void flushSpi() throws BackingStoreException {
560
            throw new UnsupportedOperationException("Should never be called.");
561
        }
562
563
        private Collection<String> getChildrenNames() {
564
            Collection<String> result = new LinkedHashSet<String>();
565
566
            if (!isRemovedNode(path)) {
567
                result.addAll(list(EL_NODE));
568
            }
569
            
570
            for (String removed : removedNodes) {
571
                int slash = removed.lastIndexOf('/');
572
                
573
                if (path.equals(removed.substring(slash))) {
574
                    result.remove(removed.substring(slash + 1));
575
                }
576
            }
577
            
578
            if (ap != null) {
579
                String prefix = toPropertyName(path, "");
580
581
                for (String key : ap.listKeys(shared)) {
582
                    if (key.startsWith(prefix)) {
583
                        String name = key.substring(prefix.length());
584
585
                        if (name.length() > 0 && name.indexOf('.') != (-1)) {
586
                            name = name.substring(0, name.indexOf('.'));
587
                            result.add(decodeString(name));
588
                        }
589
                    }
590
                }
591
            }
592
                
593
            for (String created : createdNodes) {
594
                int slash = created.lastIndexOf('/');
595
                
596
                if (path.equals(created.substring(slash))) {
597
                    result.add(created.substring(slash + 1));
598
                }
599
            }
600
601
            return result;
602
        }
603
604
        private Collection<String> list(String elementName) throws DOMException {
605
            Element dom = findRelative(path, false);
606
            
607
            if (dom == null) {
608
                return Collections.emptyList();
609
            }
610
            
611
            List<String> names = new LinkedList<String>();
612
            NodeList nl = dom.getElementsByTagNameNS(NAMESPACE, elementName);
613
614
            for (int cntr = 0; cntr < nl.getLength(); cntr++) {
615
                Node n = nl.item(cntr);
616
617
                names.add(((Element) n).getAttribute(ATTR_NAME));
618
            }
619
620
            return names;
621
        }
622
623
    }
624
    
625
    private static final class FallbackAuxiliaryPropertiesImpl implements AuxiliaryProperties {
626
627
        private static final String PREFIX = "auxiliary.";
628
        private FileObject projectDir;
629
630
        public FallbackAuxiliaryPropertiesImpl(FileObject projectDir) {
631
            this.projectDir = projectDir;
632
        }
633
        
634
        public String get(String key, boolean shared) {
635
            assert !shared;
636
            
637
            Object v = projectDir.getAttribute(PREFIX + key);
638
            
639
            return v instanceof String ? (String) v : null;
640
        }
641
642
        public void put(String key, String value, boolean shared) {
643
            assert !shared;
644
            
645
            try {
646
                projectDir.setAttribute(PREFIX + key, value);
647
            } catch (IOException ex) {
648
                Exceptions.printStackTrace(ex);
649
            }
650
        }
651
652
        public Iterable<String> listKeys(boolean shared) {
653
            assert !shared;
654
            
655
            List<String> result = new LinkedList<String>();
656
            
657
            for (Enumeration<String> en = projectDir.getAttributes(); en.hasMoreElements(); ) {
658
                String key = en.nextElement();
659
                
660
                if (key.startsWith(PREFIX)) {
661
                    key = key.substring(PREFIX.length());
662
                    
663
                    if (get(key, shared) != null) {
664
                        result.add(key);
665
                    }
666
                }
667
            }
668
            
669
            return result;
670
        }
671
        
672
    }
673
    
674
}
(-)a/projectapi/src/org/netbeans/modules/projectapi/resources/auxiliary-configuration-preferences.xsd (+78 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
5
Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
6
7
8
The contents of this file are subject to the terms of either the GNU
9
General Public License Version 2 only ("GPL") or the Common
10
Development and Distribution License("CDDL") (collectively, the
11
"License"). You may not use this file except in compliance with the
12
License. You can obtain a copy of the License at
13
http://www.netbeans.org/cddl-gplv2.html
14
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
15
specific language governing permissions and limitations under the
16
License.  When distributing the software, include this License Header
17
Notice in each file and include the License file at
18
nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
19
particular file as subject to the "Classpath" exception as provided
20
by Sun in the GPL Version 2 section of the License file that
21
accompanied this code. If applicable, add the following below the
22
License Header, with the fields enclosed by brackets [] replaced by
23
your own identifying information:
24
"Portions Copyrighted [year] [name of copyright owner]"
25
26
Contributor(s):
27
28
The Original Software is NetBeans. The Initial Developer of the Original
29
Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
30
Microsystems, Inc. All Rights Reserved.
31
32
If you wish your version of this file to be governed by only the CDDL
33
or only the GPL Version 2, indicate your decision by adding
34
"[Contributor] elects to include this software in this distribution
35
under the [CDDL or GPL Version 2] license." If you do not indicate a
36
single choice of license, a recipient has the option to distribute
37
your version of this file under either the CDDL, the GPL Version 2 or
38
to extend the choice of license to its licensees as provided above.
39
However, if you add GPL Version 2 code and therefore, elected the GPL
40
Version 2 license, then the option applies only if the new code is
41
made subject to such option by the copyright holder.
42
-->
43
44
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
45
            targetNamespace="http://www.netbeans.org/ns/auxiliary-configuration-preferences/1"
46
            xmlns="http://www.netbeans.org/ns/auxiliary-configuration-preferences/1"
47
            elementFormDefault="qualified">
48
49
    <xsd:element name="preferences">
50
        <xsd:complexType>
51
            <xsd:sequence>
52
                <xsd:element name="module" minOccurs="0" maxOccurs="unbounded" type="module-or-node"/>
53
            </xsd:sequence>
54
        </xsd:complexType>
55
    </xsd:element>
56
57
    <xsd:element name="module-or-node">
58
        <xsd:simpleContent>
59
            <xsd:attribute name="name" />
60
        </xsd:simpleContent>
61
        <xsd:complexType>
62
            <xsd:sequence>
63
                <xsd:choice minOccurs="0" maxOccurs="unbounded">
64
                    <xsd:element name="property" type="property"/>
65
                    <xsd:element name="node" type="module-or-node"/>
66
                </xsd:choice>
67
            </xsd:sequence>
68
        </xsd:complexType>
69
    </xsd:element>
70
71
    <xsd:element name="property">
72
        <xsd:simpleContent>
73
            <xsd:attribute name="name" />
74
            <xsd:attribute name="value" />
75
        </xsd:simpleContent>
76
    </xsd:element>
77
78
</xsd:schema>
(-)a/projectapi/src/org/netbeans/spi/project/AuxiliaryProperties.java (+84 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
4
 * Copyright 2008 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 2008 Sun Microsystems, Inc.
38
 */
39
40
package org.netbeans.spi.project;
41
42
/**<p>Allow to store arbitrary properties in the project, similarly as {@link AuxiliaryConfiguration}.
43
 * Used as backing store for {@link org.netbeans.api.project.ProjectUtils#getPreferences(org.netbeans.api.project.Project, java.lang.Class, boolean)}.
44
 * </p>
45
 * 
46
 * <p>Note to API clients: do not use this interface directly, use
47
 * {@link org.netbeans.api.project.ProjectUtils#getPreferences(org.netbeans.api.project.Project, java.lang.Class, boolean)} instead.
48
 * </p>
49
 * 
50
 * @author Jan Lahoda
51
 * @since 1.16
52
 */
53
public interface AuxiliaryProperties {
54
55
    /**
56
     * Get a property value.
57
     * 
58
     * @param key name of the property
59
     * @param shared true to look in a sharable settings area, false to look in a private
60
     *               settings area
61
     * @return value of the selected property, or null if not set.
62
     */
63
    public String get(String key, boolean shared);
64
    
65
    /**
66
     * Put a property value.
67
     * 
68
     * @param key name of the property
69
     * @param value value of the property. <code>null</code> will remove the property.
70
     * @param shared true to look in a sharable settings area, false to look in a private
71
     *               settings area
72
     */
73
    public void put(String key, String value, boolean shared);
74
    
75
    /**
76
     * List all keys of all known properties.
77
     * 
78
     * @param shared true to look in a sharable settings area, false to look in a private
79
     *               settings area
80
     * @return known keys.
81
     */
82
    public Iterable<String> listKeys(boolean shared);
83
    
84
}
(-)a/projectapi/test/unit/src/org/netbeans/api/project/TestUtil.java (-3 / +17 lines)
Lines 52-57 Link Here
52
import java.util.WeakHashMap;
52
import java.util.WeakHashMap;
53
import junit.framework.Assert;
53
import junit.framework.Assert;
54
import org.netbeans.junit.NbTestCase;
54
import org.netbeans.junit.NbTestCase;
55
import org.netbeans.spi.project.AuxiliaryConfiguration;
55
import org.netbeans.spi.project.ProjectFactory;
56
import org.netbeans.spi.project.ProjectFactory;
56
import org.netbeans.spi.project.ProjectState;
57
import org.netbeans.spi.project.ProjectState;
57
import org.openide.filesystems.FileObject;
58
import org.openide.filesystems.FileObject;
Lines 60-66 Link Here
60
import org.openide.filesystems.Repository;
61
import org.openide.filesystems.Repository;
61
import org.openide.filesystems.URLMapper;
62
import org.openide.filesystems.URLMapper;
62
import org.openide.util.Lookup;
63
import org.openide.util.Lookup;
64
import org.openide.util.lookup.Lookups;
63
import org.openide.util.test.MockLookup;
65
import org.openide.util.test.MockLookup;
66
import org.openide.xml.XMLUtil;
67
import org.w3c.dom.Document;
68
import org.w3c.dom.Element;
69
import org.w3c.dom.Node;
70
import org.w3c.dom.NodeList;
64
71
65
/**
72
/**
66
 * Help set up org.netbeans.api.project.*Test.
73
 * Help set up org.netbeans.api.project.*Test.
Lines 220-225 Link Here
220
     */
227
     */
221
    public static Object BROKEN_PROJECT_LOAD_LOCK = null;
228
    public static Object BROKEN_PROJECT_LOAD_LOCK = null;
222
    
229
    
230
    /**If non-null, use the value as the Lookup for newly created projects.
231
     * 
232
     */
233
    public static Lookup LOOKUP = null;
234
    
223
    private static final class TestProjectFactory implements ProjectFactory {
235
    private static final class TestProjectFactory implements ProjectFactory {
224
        
236
        
225
        TestProjectFactory() {}
237
        TestProjectFactory() {}
Lines 246-252 Link Here
246
                    }
258
                    }
247
                    throw new IOException("Load failed of " + projectDirectory);
259
                    throw new IOException("Load failed of " + projectDirectory);
248
                } else {
260
                } else {
249
                    return new TestProject(projectDirectory, state);
261
                    return new TestProject(projectDirectory, LOOKUP != null ? LOOKUP : Lookup.EMPTY, state);
250
                }
262
                }
251
            } else {
263
            } else {
252
                return null;
264
                return null;
Lines 279-295 Link Here
279
    private static final class TestProject implements Project {
291
    private static final class TestProject implements Project {
280
        
292
        
281
        private final FileObject dir;
293
        private final FileObject dir;
294
        private final Lookup lookup;
282
        final ProjectState state;
295
        final ProjectState state;
283
        Throwable error;
296
        Throwable error;
284
        int saveCount = 0;
297
        int saveCount = 0;
285
        
298
        
286
        public TestProject(FileObject dir, ProjectState state) {
299
        public TestProject(FileObject dir, Lookup lookup, ProjectState state) {
287
            this.dir = dir;
300
            this.dir = dir;
301
            this.lookup = lookup;
288
            this.state = state;
302
            this.state = state;
289
        }
303
        }
290
        
304
        
291
        public Lookup getLookup() {
305
        public Lookup getLookup() {
292
            return Lookup.EMPTY;
306
            return lookup;
293
        }
307
        }
294
        
308
        
295
        public FileObject getProjectDirectory() {
309
        public FileObject getProjectDirectory() {
(-)a/projectapi/test/unit/src/org/netbeans/modules/projectapi/AuxiliaryConfigBasedPreferencesProviderTest.java (+460 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2008 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 2008 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.projectapi;
43
44
import java.io.File;
45
import java.io.IOException;
46
import java.lang.ref.Reference;
47
import java.lang.ref.WeakReference;
48
import java.util.Arrays;
49
import java.util.Collections;
50
import java.util.Enumeration;
51
import java.util.LinkedList;
52
import java.util.List;
53
import java.util.Properties;
54
import java.util.concurrent.atomic.AtomicInteger;
55
import java.util.prefs.BackingStoreException;
56
import java.util.prefs.Preferences;
57
import org.netbeans.api.project.Project;
58
import org.netbeans.api.project.ProjectManager;
59
import org.netbeans.api.project.TestUtil;
60
import org.netbeans.junit.NbTestCase;
61
import org.netbeans.spi.project.AuxiliaryConfiguration;
62
import org.netbeans.spi.project.AuxiliaryProperties;
63
import org.openide.filesystems.FileObject;
64
import org.openide.filesystems.FileUtil;
65
import org.openide.util.Lookup;
66
import org.openide.util.lookup.Lookups;
67
import org.openide.util.lookup.ProxyLookup;
68
import org.openide.util.test.MockLookup;
69
import org.openide.xml.XMLUtil;
70
import org.w3c.dom.Document;
71
import org.w3c.dom.Element;
72
import org.w3c.dom.Node;
73
import org.w3c.dom.NodeList;
74
75
/**
76
 *
77
 * @author Jan Lahoda
78
 */
79
public class AuxiliaryConfigBasedPreferencesProviderTest extends NbTestCase {
80
    
81
    public AuxiliaryConfigBasedPreferencesProviderTest(String testName) {
82
        super(testName);
83
    }            
84
85
    private FileObject fo;
86
    private Project p;
87
    private TestLookup lookup;
88
    
89
    @Override
90
    protected void setUp() throws Exception {
91
        MockLookup.setInstances(TestUtil.testProjectFactory());
92
        clearWorkDir();
93
        File wd = getWorkDir();
94
        FileUtil.refreshAll();
95
        File f = new File(new File(wd, "test"), "testproject");
96
        FileObject testprojectFO = FileUtil.createFolder(f);
97
        assertNotNull(testprojectFO);
98
        fo = testprojectFO.getParent();
99
        TestUtil.LOOKUP = lookup = new TestLookup();
100
        p = ProjectManager.getDefault().findProject(fo);
101
        assertNotNull(p);
102
    }
103
104
    public void testStorage() throws IOException, BackingStoreException {
105
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryConfigurationImpl()));
106
        doTestStorage();
107
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryProperties()));
108
        doTestStorage();
109
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryConfigurationImpl(), new TestAuxiliaryProperties()));
110
        doTestStorage();
111
    }
112
    
113
    private void doTestStorage() throws IOException, BackingStoreException {
114
        AuxiliaryConfiguration ac = p.getLookup().lookup(AuxiliaryConfiguration.class);
115
        AuxiliaryProperties ap = p.getLookup().lookup(AuxiliaryProperties.class);
116
        
117
        assertTrue(ac != null || ap != null);
118
        
119
        AuxiliaryConfigBasedPreferencesProvider provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, true);
120
        Preferences pref = provider.findModule("test");
121
        
122
        pref.put("test", "test");
123
        
124
        pref.node("subnode1/subnode2").put("somekey", "somevalue");
125
        
126
        assertEquals(Arrays.asList("somekey"), Arrays.asList(pref.node("subnode1/subnode2").keys()));
127
        
128
        pref.flush();
129
        
130
        provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, true);
131
        pref = provider.findModule("test");
132
        
133
        assertEquals("test", pref.get("test", null));
134
        assertEquals("somevalue", pref.node("subnode1/subnode2").get("somekey", null));
135
        assertEquals(Arrays.asList("somekey"), Arrays.asList(pref.node("subnode1/subnode2").keys()));
136
        pref.node("subnode1/subnode2").remove("somekey");
137
        assertEquals(Arrays.<String>asList(), Arrays.asList(pref.node("subnode1/subnode2").keys()));
138
    }
139
140
    public void testNoSaveWhenNotModified() throws IOException, BackingStoreException {
141
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryConfigurationImpl()));
142
        
143
        AuxiliaryConfiguration ac = p.getLookup().lookup(AuxiliaryConfiguration.class);
144
        
145
        assertNotNull(ac);
146
        
147
        final AtomicInteger putCount = new AtomicInteger();
148
        
149
        ac = new CountingAuxiliaryConfiguration(ac, putCount);
150
        
151
        AuxiliaryConfigBasedPreferencesProvider provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, null, true);
152
        Preferences pref = provider.findModule("test");
153
        
154
        pref.put("test", "test");
155
        
156
        pref.node("subnode1/subnode2").put("somekey", "somevalue");
157
        
158
        assertEquals(0, putCount.get());
159
        pref.flush();
160
        assertEquals(1, putCount.get());
161
        pref.flush();
162
        assertEquals(1, putCount.get());
163
    }
164
    
165
    public void testSubnodes() throws IOException, BackingStoreException {
166
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryConfigurationImpl()));
167
        doTestSubnodes();
168
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryProperties()));
169
        doTestSubnodes();
170
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryConfigurationImpl(), new TestAuxiliaryProperties()));
171
        doTestSubnodes();
172
    }
173
    
174
    private void doTestSubnodes() throws IOException, BackingStoreException {
175
        AuxiliaryConfiguration ac = p.getLookup().lookup(AuxiliaryConfiguration.class);
176
        AuxiliaryProperties ap = p.getLookup().lookup(AuxiliaryProperties.class);
177
        
178
        assertTrue(ac != null || ap != null);
179
180
        AuxiliaryConfigBasedPreferencesProvider provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, true);
181
        Preferences pref = provider.findModule("test");
182
183
        pref.put("test", "test");
184
185
        pref.node("subnode1/subnode2").put("somekey", "somevalue1");
186
        pref.node("subnode1").put("somekey", "somevalue2");
187
188
        pref.flush();
189
        
190
        provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, true);
191
        pref = provider.findModule("test");
192
        
193
        assertTrue(pref.node("subnode1").nodeExists("subnode2"));
194
        assertEquals("somevalue1", pref.node("subnode1/subnode2").get("somekey", null));
195
        assertEquals("somevalue2", pref.node("subnode1").get("somekey", null));
196
        pref.node("subnode1").removeNode();
197
        assertEquals(null, pref.node("subnode1/subnode2").get("somekey", null));
198
        assertEquals(null, pref.node("subnode1").get("somekey", null));
199
200
        pref.flush();
201
202
        provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, true);
203
        pref = provider.findModule("test");
204
205
        assertEquals(null, pref.node("subnode1/subnode2").get("somekey", null));
206
        assertEquals(null, pref.node("subnode1").get("somekey", null));
207
    }
208
    
209
    public void testSync() throws IOException, BackingStoreException {
210
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryConfigurationImpl()));
211
        
212
        AuxiliaryConfiguration ac = p.getLookup().lookup(AuxiliaryConfiguration.class);
213
        
214
        assertNotNull(ac);
215
                
216
        AuxiliaryConfigBasedPreferencesProvider toSync = new AuxiliaryConfigBasedPreferencesProvider(p, ac, null, true);
217
        Preferences pref = toSync.findModule("test");
218
        
219
        pref.put("test", "test");
220
        
221
        pref.node("subnode1/subnode2").put("somekey", "somevalue");
222
        pref.flush();
223
        
224
        AuxiliaryConfigBasedPreferencesProvider orig = new AuxiliaryConfigBasedPreferencesProvider(p, ac, null, true);
225
        
226
        Preferences origNode = orig.findModule("test").node("subnode1/subnode2");
227
        
228
        pref.node("subnode1/subnode2").put("somekey", "somevalue2");
229
        pref.flush();
230
        
231
        origNode.sync();
232
        
233
        assertEquals("somevalue2", origNode.get("somekey", null));
234
    }
235
    
236
    public void testReclaimable() throws IOException, BackingStoreException, InterruptedException {
237
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryConfigurationImpl()));
238
        
239
        Preferences pref = AuxiliaryConfigBasedPreferencesProvider.getPreferences(p, Object.class, true);
240
        
241
        //the same preferences instance is returned as long as the previous one exists:
242
        assertTrue(pref == AuxiliaryConfigBasedPreferencesProvider.getPreferences(p, Object.class, true));
243
        
244
        //but the preferences can be reclaimed, as well as the project if noone holds them:
245
        Reference<Preferences> rPref = new WeakReference<Preferences>(pref);
246
        Reference<Project> rProject = new WeakReference<Project>(p);
247
        
248
        TestUtil.notifyDeleted(p);
249
        
250
        Thread.sleep(5000);
251
        
252
        p = null;
253
        pref = null;
254
        
255
        assertGC("", rPref);
256
        assertGC("", rProject);
257
    }
258
    
259
    public void testComplexNames() throws IOException, BackingStoreException, InterruptedException {
260
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryProperties()));
261
        
262
        AuxiliaryProperties ap = p.getLookup().lookup(AuxiliaryProperties.class);
263
        
264
        assertNotNull(ap != null);
265
266
        AuxiliaryConfigBasedPreferencesProvider provider = new AuxiliaryConfigBasedPreferencesProvider(p, null, ap, true);
267
        Preferences pref = provider.findModule("test");
268
        
269
        pref.node(".:./.:.").put(".:.", "correct");
270
        
271
        pref.flush();
272
        
273
        provider = new AuxiliaryConfigBasedPreferencesProvider(p, null, ap, true);
274
        pref = provider.findModule("test");
275
        
276
        assertTrue(pref.nodeExists(".:./.:."));
277
        assertEquals(Arrays.asList(".:."), Arrays.asList(pref.node(".:./.:.").keys()));
278
    }
279
    
280
    public void testNoAuxiliaryImplInLookup() {
281
        Preferences pref = AuxiliaryConfigBasedPreferencesProvider.getPreferences(p, Object.class, true);
282
        
283
        assertNotNull(pref);
284
        
285
        pref = AuxiliaryConfigBasedPreferencesProvider.getPreferences(p, Object.class, false);
286
        
287
        assertNotNull(pref);
288
        
289
        pref = AuxiliaryConfigBasedPreferencesProvider.getPreferences(p, Object.class, true);
290
        
291
        assertNotNull(pref);
292
    }
293
    
294
    public void testRemoves() throws IOException, BackingStoreException, InterruptedException {
295
        lookup.setDelegates(Lookups.fixed(new TestAuxiliaryConfigurationImpl(), new TestAuxiliaryProperties()));
296
        
297
        AuxiliaryConfiguration ac = p.getLookup().lookup(AuxiliaryConfiguration.class);
298
        AuxiliaryProperties ap = p.getLookup().lookup(AuxiliaryProperties.class);
299
300
        assertTrue(ac != null && ap != null);
301
        
302
        AtomicInteger putCount = new AtomicInteger();
303
        
304
        ac = new CountingAuxiliaryConfiguration(ac, putCount);
305
306
        AuxiliaryConfigBasedPreferencesProvider provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, true);
307
        Preferences pref = provider.findModule("test");
308
        
309
        pref.put("test", "test");
310
        pref.node("somenode");
311
        
312
        pref.flush();
313
        
314
        assertEquals(0, putCount.get());
315
316
        provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, true);
317
        pref = provider.findModule("test");
318
        
319
        assertEquals("test", pref.get("test", null));
320
        
321
        pref.remove("test");
322
        pref.node("somenode").removeNode();
323
        
324
        pref.flush();
325
        
326
        assertEquals(0, putCount.get());
327
        
328
        provider = new AuxiliaryConfigBasedPreferencesProvider(p, ac, ap, true);
329
        pref = provider.findModule("test");
330
        
331
        assertNull(pref.get("test", null));
332
        
333
        pref.flush();
334
        
335
        assertEquals(0, putCount.get());
336
    }
337
    
338
    private static final class TestAuxiliaryConfigurationImpl implements AuxiliaryConfiguration {
339
340
        private final Document sharedDOM;
341
        private final Document privDOM;
342
343
        public TestAuxiliaryConfigurationImpl() {
344
            sharedDOM = XMLUtil.createDocument("test", null, null, null);
345
            privDOM = XMLUtil.createDocument("test", null, null, null);
346
        }
347
        
348
        public Element getConfigurationFragment(String elementName, String namespace, boolean shared) {
349
            Element el = find(shared, namespace, elementName);
350
351
            if (el != null) {
352
                Document dummy = XMLUtil.createDocument("test", null, null, null);
353
                return (Element) dummy.importNode(el, true);
354
            }
355
            
356
            return null;
357
        }
358
359
        public void putConfigurationFragment(Element fragment, boolean shared) throws IllegalArgumentException {
360
            removeConfigurationFragment(fragment.getLocalName(), fragment.getNamespaceURI(), shared);
361
            
362
            Document dom = shared ? sharedDOM : privDOM;
363
            
364
            dom.getDocumentElement().appendChild(dom.importNode(fragment, true));
365
        }
366
367
        public boolean removeConfigurationFragment(String elementName, String namespace, boolean shared) throws IllegalArgumentException {
368
            Element el = find(shared, namespace, elementName);
369
370
            if (el != null) {
371
                el.getParentNode().removeChild(el);
372
                return true;
373
            }
374
            
375
            return false;
376
        }
377
378
        private Element find(boolean shared, String namespace, String elementName) {
379
            Document dom = shared ? sharedDOM : privDOM;
380
            NodeList nl = dom.getDocumentElement().getChildNodes();
381
            
382
            for (int cntr = 0; cntr < nl.getLength(); cntr++) {
383
                Node n = nl.item(cntr);
384
                
385
                if (n.getNodeType() == Node.ELEMENT_NODE && namespace.equals(n.getNamespaceURI()) && elementName.equals(n.getLocalName())) {
386
                    return (Element) n;
387
                }
388
            }
389
            return null;
390
        }
391
        
392
    }
393
    
394
    private static final class CountingAuxiliaryConfiguration implements AuxiliaryConfiguration {
395
        private final AuxiliaryConfiguration ac;
396
        private final AtomicInteger putCount;
397
398
        public CountingAuxiliaryConfiguration(AuxiliaryConfiguration ac, AtomicInteger putCount) {
399
            this.ac = ac;
400
            this.putCount = putCount;
401
        }
402
403
        public Element getConfigurationFragment(String elementName, String namespace, boolean shared) {
404
            return ac.getConfigurationFragment(elementName, namespace, shared);
405
        }
406
        public void putConfigurationFragment(Element fragment, boolean shared) throws IllegalArgumentException {
407
            putCount.incrementAndGet();
408
            ac.putConfigurationFragment(fragment, shared);
409
        }
410
        public boolean removeConfigurationFragment(String elementName, String namespace, boolean shared) throws IllegalArgumentException {
411
            putCount.incrementAndGet();
412
            return ac.removeConfigurationFragment(elementName, namespace, shared);
413
        }
414
    };
415
        
416
    private static final class TestAuxiliaryProperties implements AuxiliaryProperties {
417
418
        private Properties pub;
419
        private Properties priv;
420
421
        public TestAuxiliaryProperties() {
422
            this.pub = new Properties();
423
            this.priv = new Properties();
424
        }
425
        
426
        public String get(String key, boolean shared) {
427
            return (shared ? pub : priv).getProperty(key);
428
        }
429
430
        public void put(String key, String value, boolean shared) {
431
            if (value != null) {
432
                (shared ? pub : priv).setProperty(key, value);
433
            } else {
434
                (shared ? pub : priv).remove(key);
435
            }
436
        }
437
438
        public Iterable<String> listKeys(boolean shared) {
439
            Enumeration en  = (shared ? pub : priv).propertyNames();
440
            List<String> result = new LinkedList<String>();
441
            
442
            while (en.hasMoreElements()) {
443
                Object el = en.nextElement();
444
                
445
                if (el instanceof String) {
446
                    result.add((String) el);
447
                }
448
            }
449
            
450
            return Collections.unmodifiableList(result);
451
        }
452
453
    }
454
    
455
    private static final class TestLookup extends ProxyLookup {
456
        public void setDelegates(Lookup... l) {
457
            setLookups(l);
458
        }
459
    }
460
}

Return to bug 134580