001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.web.project;
043:
044: import java.beans.PropertyChangeListener;
045: import java.beans.PropertyChangeEvent;
046: import java.io.File;
047:
048: import javax.swing.event.ChangeEvent;
049: import javax.swing.event.ChangeListener;
050:
051: import org.openide.filesystems.FileUtil;
052: import org.openide.util.Mutex;
053:
054: import org.netbeans.api.project.Sources;
055: import org.netbeans.api.project.SourceGroup;
056: import org.netbeans.api.project.ProjectManager;
057: import org.netbeans.api.project.FileOwnerQuery;
058: import org.netbeans.api.java.project.JavaProjectConstants;
059: import org.netbeans.modules.java.api.common.SourceRoots;
060: import org.netbeans.modules.web.api.webmodule.WebProjectConstants;
061: import org.netbeans.modules.web.project.ui.customizer.WebProjectProperties;
062: import org.netbeans.spi.project.support.ant.SourcesHelper;
063: import org.netbeans.spi.project.support.ant.AntProjectHelper;
064: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
065: import org.openide.util.ChangeSupport;
066:
067: /**
068: * Implementation of {@link Sources} interface for WebProject.
069: */
070: public class WebSources implements Sources, PropertyChangeListener,
071: ChangeListener {
072:
073: private static final String BUILD_DIR_PROP = "${"
074: + WebProjectProperties.BUILD_DIR + "}"; //NOI18N
075: private static final String DIST_DIR_PROP = "${"
076: + WebProjectProperties.DIST_DIR + "}"; //NOI18N
077:
078: private final AntProjectHelper helper;
079: private final PropertyEvaluator evaluator;
080: private final SourceRoots sourceRoots;
081: private final SourceRoots testRoots;
082: private Sources delegate;
083: /**
084: * Flag to forbid multiple invocation of {@link SourcesHelper#registerExternalRoots}
085: **/
086: private boolean externalRootsRegistered;
087: private final ChangeSupport changeSupport = new ChangeSupport(this );
088: private SourcesHelper sourcesHelper;
089:
090: WebSources(AntProjectHelper helper, PropertyEvaluator evaluator,
091: SourceRoots sourceRoots, SourceRoots testRoots) {
092: this .helper = helper;
093: this .evaluator = evaluator;
094: this .sourceRoots = sourceRoots;
095: this .testRoots = testRoots;
096: this .sourceRoots.addPropertyChangeListener(this );
097: this .testRoots.addPropertyChangeListener(this );
098: this .evaluator.addPropertyChangeListener(this );
099: initSources(); // have to register external build roots eagerly
100: }
101:
102: /**
103: * Returns an array of SourceGroup of given type. It delegates to {@link SourcesHelper}.
104: * This method firstly acquire the {@link ProjectManager#mutex} in read mode then it enters
105: * into the synchronized block to ensure that just one instance of the {@link SourcesHelper}
106: * is created. These instance is cleared also in the synchronized block by the
107: * {@link WebSources#fireChange} method.
108: */
109: public SourceGroup[] getSourceGroups(final String type) {
110: return (SourceGroup[]) ProjectManager.mutex().readAccess(
111: new Mutex.Action() {
112: public Object run() {
113: Sources _delegate;
114: synchronized (WebSources.this ) {
115: if (delegate == null) {
116: delegate = initSources();
117: delegate
118: .addChangeListener(WebSources.this );
119: }
120: _delegate = delegate;
121: }
122: return _delegate.getSourceGroups(type);
123: }
124: });
125: }
126:
127: private Sources initSources() {
128: sourcesHelper = new SourcesHelper(helper, evaluator);
129: File projectDir = FileUtil.toFile(this .helper
130: .getProjectDirectory());
131: String[] propNames = sourceRoots.getRootProperties();
132: String[] rootNames = sourceRoots.getRootNames();
133: for (int i = 0; i < propNames.length; i++) {
134: String displayName = rootNames[i];
135: String prop = "${" + propNames[i] + "}";
136: if (displayName.length() == 0) {
137: //If the prop is src.dir use the default name
138: if ("src.dir".equals(propNames[i])) { //NOI18N
139: displayName = SourceRoots.DEFAULT_SOURCE_LABEL;
140: } else {
141: //If the name is not given, it should be either a relative path in the project dir
142: //or absolute path when the root is not under the project dir
143: File sourceRoot = helper.resolveFile(evaluator
144: .evaluate(prop));
145: if (sourceRoot != null) {
146: String srPath = sourceRoot.getAbsolutePath();
147: String pdPath = projectDir.getAbsolutePath()
148: + File.separatorChar;
149: if (srPath.startsWith(pdPath)) {
150: displayName = srPath.substring(pdPath
151: .length());
152: } else {
153: displayName = sourceRoot.getAbsolutePath();
154: }
155: } else {
156: displayName = SourceRoots.DEFAULT_SOURCE_LABEL;
157: }
158: }
159: }
160: sourcesHelper.addPrincipalSourceRoot(prop, displayName, /*XXX*/
161: null, null);
162: sourcesHelper.addTypedSourceRoot(prop,
163: JavaProjectConstants.SOURCES_TYPE_JAVA,
164: displayName, /*XXX*/null, null);
165: }
166: propNames = testRoots.getRootProperties();
167: rootNames = testRoots.getRootNames();
168: for (int i = 0; i < propNames.length; i++) {
169: String displayName = rootNames[i];
170: String prop = "${" + propNames[i] + "}";
171: if (displayName.length() == 0) {
172: //If the prop is test.src.dir use the default name
173: if ("test.src.dir".equals(propNames[i])) { //NOI18N
174: displayName = SourceRoots.DEFAULT_TEST_LABEL;
175: } else {
176: //If the name is not given, it should be either a relative path in the project dir
177: //or absolute path when the root is not under the project dir
178: File sourceRoot = helper.resolveFile(evaluator
179: .evaluate(prop));
180: if (sourceRoot != null) {
181: String srPath = sourceRoot.getAbsolutePath();
182: String pdPath = projectDir.getAbsolutePath()
183: + File.separatorChar;
184: if (srPath.startsWith(pdPath)) {
185: displayName = srPath.substring(pdPath
186: .length());
187: } else {
188: displayName = sourceRoot.getAbsolutePath();
189: }
190: } else {
191: displayName = SourceRoots.DEFAULT_TEST_LABEL;
192: }
193: }
194: }
195: sourcesHelper.addPrincipalSourceRoot(prop, displayName, /*XXX*/
196: null, null);
197: sourcesHelper.addTypedSourceRoot(prop,
198: JavaProjectConstants.SOURCES_TYPE_JAVA,
199: displayName, /*XXX*/null, null);
200: }
201:
202: //Web Pages
203: String webModuleLabel = org.openide.util.NbBundle.getMessage(
204: WebSources.class, "LBL_Node_WebModule"); //NOI18N
205: String webPagesLabel = org.openide.util.NbBundle.getMessage(
206: WebSources.class, "LBL_Node_DocBase"); //NOI18N
207: String webInfLabel = org.openide.util.NbBundle.getMessage(
208: WebSources.class, "LBL_Node_WebInf"); //NOI18N
209: sourcesHelper.addPrincipalSourceRoot("${"
210: + WebProjectProperties.SOURCE_ROOT + "}",
211: webModuleLabel, /*XXX*/null, null); //NOI18N
212: sourcesHelper.addPrincipalSourceRoot("${"
213: + WebProjectProperties.WEB_DOCBASE_DIR + "}",
214: webPagesLabel, /*XXX*/null, null); //NOI18N
215: sourcesHelper.addTypedSourceRoot("${"
216: + WebProjectProperties.WEB_DOCBASE_DIR + "}",
217: WebProjectConstants.TYPE_DOC_ROOT, webPagesLabel, /*XXX*/
218: null, null); //NOI18N
219: // sourcesHelper.addTypedSourceRoot("${"+ WebProjectProperties.WEB_DOCBASE_DIR+"}/WEB-INF", WebProjectConstants.TYPE_WEB_INF, /*XXX I18N*/ "WEB-INF", /*XXX*/null, null); //NOI18N
220: sourcesHelper.addTypedSourceRoot("${"
221: + WebProjectProperties.WEBINF_DIR + "}",
222: WebProjectConstants.TYPE_WEB_INF, webInfLabel, /*XXX*/
223: null, null); //NOI18N
224:
225: sourcesHelper.addNonSourceRoot(BUILD_DIR_PROP);
226: sourcesHelper.addNonSourceRoot(DIST_DIR_PROP);
227:
228: externalRootsRegistered = false;
229: ProjectManager.mutex().postWriteRequest(new Runnable() {
230: public void run() {
231: if (!externalRootsRegistered) {
232: sourcesHelper
233: .registerExternalRoots(FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT);
234: externalRootsRegistered = true;
235: }
236: }
237: });
238: return sourcesHelper.createSources();
239: }
240:
241: public void addChangeListener(ChangeListener changeListener) {
242: changeSupport.addChangeListener(changeListener);
243: }
244:
245: public void removeChangeListener(ChangeListener changeListener) {
246: changeSupport.removeChangeListener(changeListener);
247: }
248:
249: private void fireChange() {
250: synchronized (this ) {
251: delegate = null;
252: }
253: changeSupport.fireChange();
254: }
255:
256: public void propertyChange(PropertyChangeEvent evt) {
257: String propName = evt.getPropertyName();
258: if (SourceRoots.PROP_ROOT_PROPERTIES.equals(propName)
259: || WebProjectProperties.BUILD_DIR.equals(propName)
260: || WebProjectProperties.DIST_DIR.equals(propName))
261: this .fireChange();
262: }
263:
264: public void stateChanged(ChangeEvent event) {
265: this.fireChange();
266: }
267:
268: }
|