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

(-)a/projectapi/src/org/netbeans/api/project/ProjectUtils.java (+22 lines)
Lines 49-56 Link Here
49
import java.util.Set;
49
import java.util.Set;
50
import javax.swing.Icon;
50
import javax.swing.Icon;
51
import javax.swing.ImageIcon;
51
import javax.swing.ImageIcon;
52
import org.netbeans.modules.projectapi.AuxiliaryConfigImpl;
53
import org.netbeans.spi.project.AuxiliaryConfiguration;
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;
56
import org.openide.filesystems.FileObject;
54
import org.openide.filesystems.FileStateInvalidException;
57
import org.openide.filesystems.FileStateInvalidException;
55
import org.openide.util.Mutex;
58
import org.openide.util.Mutex;
56
import org.openide.util.Utilities;
59
import org.openide.util.Utilities;
Lines 215-218 Link Here
215
        
218
        
216
    }
219
    }
217
    
220
    
221
    /**
222
     * Find a way of storing extra configuration in a project.
223
     * If the project's {@linkplain Project#getLookup lookup} does not provide an instance,
224
     * a fallback implementation is used.
225
     * <p class="nonnormative">
226
     * The current fallback implementation uses {@linkplain FileObject#setAttribute file attributes}
227
     * for "nonsharable" configuration, and a specially named file in the project directory
228
     * for "sharable" configuration. For compatibility purposes (in case a project adds an
229
     * {@link AuxiliaryConfiguration} instance to its lookup where before it had none),
230
     * the fallback storage is read (but not written) even if there is an instance in project lookup.
231
     * </p>
232
     * @param project a project
233
     * @return an auxiliary configuration handle
234
     * @since XXX
235
     */
236
    public static AuxiliaryConfiguration getAuxiliaryConfiguration(Project project) {
237
        return new AuxiliaryConfigImpl(project);
238
    }
239
218
}
240
}
(-)a/projectapi/src/org/netbeans/modules/projectapi/AuxiliaryConfigImpl.java (+272 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.modules.projectapi;
41
42
import java.io.IOException;
43
import java.io.InputStream;
44
import java.io.OutputStream;
45
import java.io.StringReader;
46
import java.util.logging.Level;
47
import java.util.logging.Logger;
48
import javax.xml.parsers.DocumentBuilderFactory;
49
import org.netbeans.api.project.Project;
50
import org.netbeans.api.project.ProjectManager;
51
import org.netbeans.spi.project.AuxiliaryConfiguration;
52
import org.openide.filesystems.FileObject;
53
import org.openide.util.Mutex;
54
import org.openide.xml.XMLUtil;
55
import org.w3c.dom.Document;
56
import org.w3c.dom.Element;
57
import org.w3c.dom.Node;
58
import org.w3c.dom.NodeList;
59
import org.w3c.dom.ls.DOMImplementationLS;
60
import org.w3c.dom.ls.LSSerializer;
61
import org.xml.sax.InputSource;
62
import org.xml.sax.SAXException;
63
64
public class AuxiliaryConfigImpl implements AuxiliaryConfiguration {
65
66
    private static final Logger LOG = Logger.getLogger(AuxiliaryConfigImpl.class.getName());
67
    private static final String AUX_CONFIG_ATTR_BASE = AuxiliaryConfiguration.class.getName();
68
    private static final String AUX_CONFIG_FILENAME = ".netbeans.xml"; // NOI18N
69
70
    private final Project project;
71
72
    public AuxiliaryConfigImpl(Project proj) {
73
        this.project = proj;
74
    }
75
76
    public Element getConfigurationFragment(final String elementName, final String namespace, final boolean shared) {
77
        return ProjectManager.mutex().readAccess(new Mutex.Action<Element>() {
78
            public Element run() {
79
                AuxiliaryConfiguration delegate = project.getLookup().lookup(AuxiliaryConfiguration.class);
80
                if (delegate != null) {
81
                    Element fragment = delegate.getConfigurationFragment(elementName, namespace, shared);
82
                    if (fragment != null) {
83
                        return fragment;
84
                    }
85
                }
86
                FileObject dir = project.getProjectDirectory();
87
                if (shared) {
88
                    FileObject config = dir.getFileObject(AUX_CONFIG_FILENAME);
89
                    if (config != null) {
90
                        try {
91
                            InputStream is = config.getInputStream();
92
                            try {
93
                                InputSource input = new InputSource(is);
94
                                input.setSystemId(config.getURL().toString());
95
                                Element root = XMLUtil.parse(input, false, true, /*XXX*/null, null).getDocumentElement();
96
                                return findElement(root, elementName, namespace);
97
                            } finally {
98
                                is.close();
99
                            }
100
                        } catch (Exception x) {
101
                            LOG.log(Level.INFO, "Cannot parse" + config, x);
102
                        }
103
                    }
104
                } else {
105
                    String attrName = AUX_CONFIG_ATTR_BASE + "." + namespace + "#" + elementName;
106
                    Object attr = dir.getAttribute(attrName);
107
                    if (attr instanceof String) {
108
                        try {
109
                            Element fragment = XMLUtil.parse(new InputSource(new StringReader((String) attr)), false, true,
110
                                                             /*XXX need utility method*/null, null).getDocumentElement();
111
                            // XXX check for correct namespace and name
112
                            return fragment;
113
                        } catch (SAXException x) {
114
                            LOG.log(Level.INFO, "Cannot parse value " + attr + " of " + attrName + " on " + dir + ": " + x.getMessage());
115
                        } catch (IOException x) {
116
                            assert false : x;
117
                        }
118
                    }
119
                }
120
                return null;
121
            }
122
        });
123
    }
124
125
    public void putConfigurationFragment(final Element fragment, final boolean shared) throws IllegalArgumentException {
126
        ProjectManager.mutex().writeAccess(new Mutex.Action<Void>() {
127
            public Void run() {
128
                String elementName = fragment.getLocalName();
129
                String namespace = fragment.getNamespaceURI();
130
                if (namespace == null) {
131
                    throw new IllegalArgumentException();
132
                }
133
                AuxiliaryConfiguration delegate = project.getLookup().lookup(AuxiliaryConfiguration.class);
134
                if (delegate != null) {
135
                    delegate.putConfigurationFragment(fragment, shared);
136
                    removeFallbackImpl(elementName, namespace, shared);
137
                    return null;
138
                }
139
                FileObject dir = project.getProjectDirectory();
140
                try {
141
                    if (shared) {
142
                        Document doc;
143
                        FileObject config = dir.getFileObject(AUX_CONFIG_FILENAME);
144
                        if (config != null) {
145
                            InputStream is = config.getInputStream();
146
                            try {
147
                                InputSource input = new InputSource(is);
148
                                input.setSystemId(config.getURL().toString());
149
                                doc = XMLUtil.parse(input, false, true, /*XXX*/ null, null);
150
                            } finally {
151
                                is.close();
152
                            }
153
                        } else {
154
                            config = dir.createData(AUX_CONFIG_FILENAME);
155
                            doc = XMLUtil.createDocument("auxiliary-configuration", "XXX", null, null);
156
                        }
157
                        Element root = doc.getDocumentElement();
158
                        Element newFragment = (Element) doc.importNode(fragment, true);
159
                        Element oldFragment = findElement(root, elementName, namespace);
160
                        if (oldFragment != null) {
161
                            root.replaceChild(newFragment, oldFragment);
162
                        } else {
163
                            root.appendChild(newFragment);
164
                        }
165
                        OutputStream os = config.getOutputStream();
166
                        try {
167
                            XMLUtil.write(doc, os, "UTF-8");
168
                        } finally {
169
                            os.close();
170
                        }
171
                    } else {
172
                        String attrName = AUX_CONFIG_ATTR_BASE + "." + namespace + "#" + elementName;
173
                        Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
174
                        DOMImplementationLS ls = (DOMImplementationLS) doc.getImplementation().getFeature("LS", "3.0"); // NOI18N
175
                        assert ls != null : "No DOM 3 LS supported in " + doc.getClass().getName();
176
                        // JAXP bug #6710755: cannot directly serialize fragment in JDK 5, must add to a document.
177
                        doc.appendChild(doc.importNode(fragment, true));
178
                        LSSerializer serializer = ls.createLSSerializer();
179
                        serializer.getDomConfig().setParameter("xml-declaration", false);
180
                        dir.setAttribute(attrName, serializer.writeToString(doc));
181
                    }
182
                } catch (Exception x) {
183
                    LOG.log(Level.WARNING, "Cannot save configuration to " + dir, x);
184
                }
185
                return null;
186
            }
187
        });
188
    }
189
190
    private boolean removeFallbackImpl(final String elementName, final String namespace, final boolean shared) {
191
        FileObject dir = project.getProjectDirectory();
192
        try {
193
            if (shared) {
194
                FileObject config = dir.getFileObject(AUX_CONFIG_FILENAME);
195
                if (config != null) {
196
                    try {
197
                        Document doc;
198
                        InputStream is = config.getInputStream();
199
                        try {
200
                            InputSource input = new InputSource(is);
201
                            input.setSystemId(config.getURL().toString());
202
                            doc = XMLUtil.parse(input, false, true, /*XXX*/ null, null);
203
                        } finally {
204
                            is.close();
205
                        }
206
                        Element root = doc.getDocumentElement();
207
                        Element toRemove = findElement(root, elementName, namespace);
208
                        if (toRemove != null) {
209
                            root.removeChild(toRemove);
210
                            if (root.getElementsByTagName("*").getLength() > 0) {
211
                                OutputStream os = config.getOutputStream();
212
                                try {
213
                                    XMLUtil.write(doc, os, "UTF-8");
214
                                } finally {
215
                                    os.close();
216
                                }
217
                            } else {
218
                                config.delete();
219
                            }
220
                            return true;
221
                        }
222
                    } catch (SAXException x) {
223
                        LOG.log(Level.INFO, "Cannot parse" + config, x);
224
                    }
225
                }
226
            } else {
227
                String attrName = AUX_CONFIG_ATTR_BASE + "." + namespace + "#" + elementName;
228
                if (dir.getAttribute(attrName) != null) {
229
                    dir.setAttribute(attrName, null);
230
                    return true;
231
                }
232
            }
233
        } catch (IOException x) {
234
            LOG.warning("Cannot remove configuration from " + dir);
235
        }
236
        return false;
237
    }
238
239
    public boolean removeConfigurationFragment(final String elementName, final String namespace, final boolean shared) throws IllegalArgumentException {
240
        return ProjectManager.mutex().writeAccess(new Mutex.Action<Boolean>() {
241
            public Boolean run() {
242
                AuxiliaryConfiguration delegate = project.getLookup().lookup(AuxiliaryConfiguration.class);
243
                boolean result = false;
244
                if (delegate != null) {
245
                    result |= delegate.removeConfigurationFragment(elementName, namespace, shared);
246
                }
247
                result |= removeFallbackImpl(elementName, namespace, shared);
248
                return result;
249
            }
250
        });
251
    }
252
253
    private static Element findElement(Element parent, String name, String namespace) {
254
        Element result = null;
255
        NodeList l = parent.getChildNodes();
256
        int len = l.getLength();
257
        for (int i = 0; i < len; i++) {
258
            if (l.item(i).getNodeType() == Node.ELEMENT_NODE) {
259
                Element el = (Element) l.item(i);
260
                if (name.equals(el.getLocalName()) && namespace.equals(el.getNamespaceURI())) {
261
                    if (result == null) {
262
                        result = el;
263
                    } else {
264
                        return null;
265
                    }
266
                }
267
            }
268
        }
269
        return result;
270
    }
271
272
}
(-)a/projectapi/src/org/netbeans/spi/project/AuxiliaryConfiguration.java (+5 lines)
Lines 41-46 Link Here
41
41
42
package org.netbeans.spi.project;
42
package org.netbeans.spi.project;
43
43
44
import org.netbeans.api.project.ProjectUtils;
44
import org.w3c.dom.Element;
45
import org.w3c.dom.Element;
45
46
46
/**
47
/**
Lines 57-62 Link Here
57
 * unless it is explicitly given permission to read and/or write other fragments
58
 * unless it is explicitly given permission to read and/or write other fragments
58
 * owned by another module. XML namespaces should be used to scope the data
59
 * owned by another module. XML namespaces should be used to scope the data
59
 * to avoid accidental clashes.
60
 * to avoid accidental clashes.
61
 * </p>
62
 * <p>
63
 * <strong>Do not look for this object directly in project lookup.</strong>
64
 * Instead use {@link ProjectUtils#getAuxiliaryConfiguration}.
60
 * </p>
65
 * </p>
61
 * @see org.netbeans.api.project.Project#getLookup
66
 * @see org.netbeans.api.project.Project#getLookup
62
 * @author Jesse Glick
67
 * @author Jesse Glick
(-)a/projectapi/test/unit/src/org/netbeans/modules/projectapi/AuxiliaryConfigImplTest.java (+96 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.modules.projectapi;
41
42
import javax.xml.parsers.DocumentBuilderFactory;
43
import org.netbeans.api.project.Project;
44
import org.netbeans.api.project.ProjectUtils;
45
import org.netbeans.junit.NbTestCase;
46
import org.netbeans.spi.project.AuxiliaryConfiguration;
47
import org.openide.filesystems.FileObject;
48
import org.openide.filesystems.FileUtil;
49
import org.openide.util.Lookup;
50
import org.w3c.dom.Document;
51
import org.w3c.dom.Element;
52
53
public class AuxiliaryConfigImplTest extends NbTestCase {
54
55
    public AuxiliaryConfigImplTest(String name) {
56
        super(name);
57
    }
58
59
    public void testFallbackAuxiliaryConfiguration() throws Exception {
60
        final FileObject prjdir = FileUtil.createMemoryFileSystem().getRoot();
61
        class Prj implements Project {
62
            public FileObject getProjectDirectory() {
63
                return prjdir;
64
            }
65
            public Lookup getLookup() {
66
                return Lookup.EMPTY;
67
            }
68
        }
69
        Project prj = new Prj();
70
        AuxiliaryConfiguration ac = ProjectUtils.getAuxiliaryConfiguration(prj);
71
        assertNotNull(ac);
72
        String namespace = "http://nowhere.net/test";
73
        assertNull(ac.getConfigurationFragment("x", namespace, true));
74
        ac.putConfigurationFragment(makeElement("x", namespace), true);
75
        Element e = ac.getConfigurationFragment("x", namespace, true);
76
        assertNotNull(e);
77
        assertEquals("x", e.getLocalName());
78
        assertEquals(namespace, e.getNamespaceURI());
79
        assertNull(ac.getConfigurationFragment("x", namespace, false));
80
        ac.removeConfigurationFragment("x", namespace, true);
81
        assertNull(ac.getConfigurationFragment("x", namespace, true));
82
        ac.putConfigurationFragment(makeElement("y", namespace), false);
83
        prj = new Prj();
84
        ac = ProjectUtils.getAuxiliaryConfiguration(prj);
85
        e = ac.getConfigurationFragment("y", namespace, false);
86
        assertNotNull(e);
87
        assertEquals("y", e.getLocalName());
88
        assertEquals(namespace, e.getNamespaceURI());
89
    }
90
91
    private static Element makeElement(String name, String namespace) throws Exception {
92
        Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
93
        return doc.createElementNS(namespace, name);
94
    }
95
96
}

Return to bug 136333