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: * Portions Copyrighted 2007 Sun Microsystems, Inc.
027: */
028: package org.netbeans.modules.php.project;
029:
030: import java.beans.PropertyChangeEvent;
031: import java.beans.PropertyChangeListener;
032: import java.util.ArrayList;
033: import java.util.List;
034: import java.util.Map.Entry;
035: import javax.swing.event.ChangeEvent;
036: import javax.swing.event.ChangeListener;
037: import org.netbeans.api.project.FileOwnerQuery;
038: import org.netbeans.api.project.ProjectManager;
039: import org.netbeans.api.project.SourceGroup;
040: import org.netbeans.api.project.Sources;
041: import org.netbeans.spi.project.support.ant.AntProjectEvent;
042: import org.netbeans.spi.project.support.ant.AntProjectHelper;
043: import org.netbeans.spi.project.support.ant.EditableProperties;
044: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
045: import org.netbeans.spi.project.support.ant.SourcesHelper;
046: import org.openide.util.Mutex;
047: import org.openide.util.NbBundle;
048:
049: /**
050: * Php Sources class.
051: * Is a wrapper for Sources created using 'new SourcesHelper(AntProjectHelper, PropertyEvaluator).createSources()'.
052: * Is created to add possibility to reload Sources object stored into Project's lookup.<br>
053: * Implements ChangeListener to react on wrapped Sourses.<br>
054: * Implements AntProjectListener to react on modified properties file.<br>
055: * @author avk
056: */
057: public class PhpSources implements Sources, ChangeListener,
058: PropertyChangeListener {
059:
060: private final AntProjectHelper myHelper;
061: private PropertyEvaluator myEvaluator;
062:
063: private SourcesHelper sourcesHelper;
064: private Sources delegate;
065: /**
066: * Flag to forbid multiple invocation of {@link SourcesHelper#registerExternalRoots}
067: **/
068: private boolean externalRootsRegistered;
069: private final List<ChangeListener> listeners = new ArrayList<ChangeListener>();
070:
071: public PhpSources(AntProjectHelper helper,
072: PropertyEvaluator evaluator) {
073: this .myHelper = helper;
074: this .myEvaluator = evaluator;
075:
076: myEvaluator.addPropertyChangeListener(this );
077:
078: initSources(); // have to register external build roots eagerly
079: }
080:
081: public SourceGroup[] getSourceGroups(final String type) {
082: return ProjectManager.mutex().readAccess(
083: new Mutex.Action<SourceGroup[]>() {
084:
085: public SourceGroup[] run() {
086: Sources _delegate;
087: synchronized (PhpSources.this ) {
088: if (delegate == null) {
089: delegate = initSources();
090: delegate
091: .addChangeListener(PhpSources.this );
092: }
093: _delegate = delegate;
094: }
095: return _delegate.getSourceGroups(type);
096: }
097: });
098: }
099:
100: public void addChangeListener(ChangeListener changeListener) {
101: synchronized (listeners) {
102: listeners.add(changeListener);
103: }
104: }
105:
106: public void removeChangeListener(ChangeListener changeListener) {
107: synchronized (listeners) {
108: listeners.remove(changeListener);
109: }
110: }
111:
112: private Sources initSources() {
113: this .sourcesHelper = new SourcesHelper(myHelper, myEvaluator);
114: /*
115: * Main source root config.
116: */
117: String label = NbBundle.getMessage(PhpProject.class,
118: PhpProject.SOURCE_LBL);
119: sourcesHelper.addPrincipalSourceRoot(PhpProject.SRC_DIR, label,
120: null, null);
121:
122: List<String> labels = new ArrayList<String>();
123: List<String> roots = new ArrayList<String>();
124: readSources(labels, roots);
125: for (int i = 0; i < labels.size(); i++) {
126: sourcesHelper.addPrincipalSourceRoot(roots.get(i), labels
127: .get(i), null, null);
128: sourcesHelper.addTypedSourceRoot(roots.get(i),
129: PhpProject.SOURCES_TYPE_PHP, labels.get(i), null,
130: null);
131: }
132:
133: externalRootsRegistered = false;
134: ProjectManager.mutex().postWriteRequest(new Runnable() {
135:
136: public void run() {
137: if (!externalRootsRegistered) {
138: sourcesHelper
139: .registerExternalRoots(FileOwnerQuery.EXTERNAL_ALGORITHM_TRANSIENT);
140: externalRootsRegistered = true;
141: }
142: }
143: });
144: return this .sourcesHelper.createSources();
145: }
146:
147: private void readSources(final List<String> labels,
148: final List<String> roots) {
149: ProjectManager.mutex().readAccess(new Mutex.Action<Object>() {
150:
151: public Object run() {
152: EditableProperties props = myHelper
153: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
154: for (Entry<String, String> entry : props.entrySet()) {
155: String key = entry.getKey();
156: String value = entry.getValue();
157: if (key.equals(PhpProject.SRC_DIR)) {
158: continue;
159: }
160: if (key.startsWith(PhpProject.SRC_)
161: && key.endsWith(PhpProject._DIR)) {
162: String lbl = key.substring(
163: PhpProject.SRC_.length()).substring(0,
164: PhpProject._DIR.length());
165: labels.add(lbl);
166: roots.add(value);
167: }
168: continue;
169: }
170: return null;
171: }
172: });
173: }
174:
175: /*
176: * implementation of AntProjectListener.
177: * Is not used now because we do not store data in project xml file
178: * (e.g. customizer doesn't update this file)
179: */
180: public void configurationXmlChanged(AntProjectEvent ev) {
181: // PhpSources is not interested in xml changes
182: }
183:
184: /** impl of PropertyChangeListener.
185: * Is used to listen updates in project properties file
186: * (e.g. if customizer updates this file)
187: */
188: public void propertyChange(PropertyChangeEvent evt) {
189: String property = evt.getPropertyName();
190: if (property.startsWith(PhpProject.SRC_)
191: && property.endsWith(PhpProject._DIR)) {
192: this .fireChange();
193: }
194: }
195:
196: /*
197: * implementation of ChangetListener.
198: * Is used to listen for changes in the real Sources object,
199: * wrapped by this one.
200: */
201: public void stateChanged(ChangeEvent e) {
202: this .fireChange();
203: }
204:
205: private void fireChange() {
206: ChangeListener[] _listeners;
207: synchronized (this ) {
208: if (delegate != null) {
209: delegate.removeChangeListener(this );
210: delegate = null;
211: }
212: }
213: synchronized (listeners) {
214: if (listeners.isEmpty()) {
215: return;
216: }
217: _listeners = listeners.toArray(new ChangeListener[listeners
218: .size()]);
219: }
220: ChangeEvent ev = new ChangeEvent(this );
221: for (ChangeListener l : _listeners) {
222: l.stateChanged(ev);
223: }
224: }
225:
226: }
|