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.mobility.project.queries;
043:
044: import org.netbeans.modules.mobility.project.ProjectConfigurationsHelper;
045: import java.beans.PropertyChangeEvent;
046: import java.beans.PropertyChangeListener;
047: import java.io.File;
048: import java.lang.ref.Reference;
049: import java.lang.ref.WeakReference;
050: import java.util.ArrayList;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.WeakHashMap;
054: import javax.swing.event.ChangeEvent;
055: import javax.swing.event.ChangeListener;
056: import org.netbeans.api.queries.FileBuiltQuery;
057: import org.netbeans.modules.mobility.project.DefaultPropertiesDescriptor;
058: import org.netbeans.spi.project.support.ant.AntProjectHelper;
059: import org.netbeans.spi.queries.FileBuiltQueryImplementation;
060: import org.openide.filesystems.FileAttributeEvent;
061: import org.openide.filesystems.FileChangeListener;
062: import org.openide.filesystems.FileEvent;
063: import org.openide.filesystems.FileObject;
064: import org.openide.filesystems.FileRenameEvent;
065: import org.openide.filesystems.FileUtil;
066: import org.openide.loaders.DataObject;
067: import org.openide.loaders.DataObjectNotFoundException;
068: import org.openide.util.RequestProcessor;
069: import org.openide.util.Utilities;
070: import org.openide.util.WeakListeners;
071:
072: /**
073: * @author Adam Sotona
074: */
075: public class FileBuiltQueryImpl implements
076: FileBuiltQueryImplementation, PropertyChangeListener, Runnable {
077:
078: protected final AntProjectHelper helper;
079: private static final Object NONE = "NONE"; // NOI18N
080: private final Map<FileObject, Object> statuses = new WeakHashMap<FileObject, Object>();
081: private FileObject srcRoot = null;
082:
083: public FileBuiltQueryImpl(AntProjectHelper helper,
084: ProjectConfigurationsHelper confs) {
085: this .helper = helper;
086: confs.addPropertyChangeListener(this );
087: }
088:
089: protected FileObject getSrcRoot() {
090: if (srcRoot == null) {
091: final String dir = helper.getStandardPropertyEvaluator()
092: .getProperty(DefaultPropertiesDescriptor.SRC_DIR);
093: if (dir != null)
094: srcRoot = helper.resolveFileObject(dir);
095: }
096: return srcRoot;
097: }
098:
099: public FileBuiltQuery.Status getStatus(final FileObject file) {
100: Object o;
101: synchronized (statuses) {
102: o = statuses.get(file);
103: }
104: if (o == NONE) {
105: return null;
106: }
107: final Reference r = (Reference) o;
108: StatusImpl status = (r != null) ? (StatusImpl) r.get() : null;
109: if (status == null) {
110: status = createStatus(file);
111: synchronized (statuses) {
112: if (status != null) {
113: statuses.put(file, new WeakReference<StatusImpl>(
114: status));
115: } else {
116: statuses.put(file, NONE);
117: }
118: }
119: }
120: return status;
121: }
122:
123: public void propertyChange(@SuppressWarnings("unused")
124: final PropertyChangeEvent evt) {
125: RequestProcessor.getDefault().post(this );
126: }
127:
128: public void run() {
129: FileObject files[];
130: synchronized (statuses) {
131: files = statuses.keySet().toArray(
132: new FileObject[statuses.size()]);
133: }
134: for (int i = 0; i < files.length; i++) {
135: final StatusImpl status = (StatusImpl) getStatus(files[i]);
136: if (status != null)
137: status.isBuilt();
138: }
139: }
140:
141: private StatusImpl createStatus(final FileObject file) {
142: final FileObject root = getSrcRoot();
143: if (root != null && file != null
144: && FileUtil.isParentOf(root, file)
145: && file.getExt().equals("java"))
146: try { //NOI18N
147: return new StatusImpl(file);
148: } catch (DataObjectNotFoundException dnfe) {
149: }
150: return null;
151: }
152:
153: private final class StatusImpl implements FileBuiltQuery.Status,
154: PropertyChangeListener/*<DataObject>*/,
155: FileChangeListener, Runnable {
156:
157: private final List<ChangeListener> listeners = new ArrayList<ChangeListener>();
158: private Boolean built = null;
159: private final DataObject source;
160: private FileObject oldTarget = null;
161: private FileChangeListener weekListener = null;
162:
163: StatusImpl(FileObject source)
164: throws DataObjectNotFoundException {
165: this .source = DataObject.find(source);
166: this .source.addPropertyChangeListener(WeakListeners
167: .propertyChange(this , this .source));
168: source.addFileChangeListener(FileUtil
169: .weakFileChangeListener(this , source));
170: }
171:
172: private synchronized File getTarget() {
173: final FileObject root = getSrcRoot();
174: final FileObject srcFile = source.getPrimaryFile();
175: if (root == null || srcFile == null)
176: return null;
177: final String path = FileUtil.getRelativePath(root, srcFile);
178: final String buildClasses = helper
179: .getStandardPropertyEvaluator().getProperty(
180: "build.classes.dir"); //NOI18N
181: final File target = (path == null
182: || !path.endsWith(".java") || buildClasses == null) ? null
183: : helper.resolveFile(buildClasses + "/"
184: + path.substring(0, path.length() - 4)
185: + "class"); //NOI18N
186: FileObject newTarget = null;
187: File f = target;
188: while ((newTarget == null || !newTarget.isValid())
189: && f != null) {
190: newTarget = FileUtil.toFileObject(f);
191: f = f.getParentFile();
192: }
193: if (!Utilities.compareObjects(oldTarget, newTarget)) {
194: if (oldTarget != null && weekListener != null)
195: oldTarget.removeFileChangeListener(weekListener);
196: if (newTarget != null) {
197: weekListener = FileUtil.weakFileChangeListener(
198: this , newTarget);
199: newTarget.addFileChangeListener(weekListener);
200: newTarget.getChildren(); //to kick the folder to listen for a new stuff
201: }
202: oldTarget = newTarget;
203: }
204: return target;
205: }
206:
207: // Side effect is to update its cache and maybe fire changes.
208: public boolean isBuilt() {
209: boolean doFire = false;
210: boolean b;
211: synchronized (StatusImpl.this ) {
212: b = isReallyBuilt();
213: if (built != null && built.booleanValue() != b) {
214: doFire = true;
215: }
216: built = Boolean.valueOf(b);
217: }
218: if (doFire) {
219: fireChange();
220: }
221: return b;
222: }
223:
224: private boolean isReallyBuilt() {
225: final File target = getTarget();
226: final FileObject srcFile = source.getPrimaryFile();
227: if (!source.isValid() || source.isModified()
228: || target == null || srcFile == null)
229: return false;
230: return target.lastModified() >= srcFile.lastModified()
231: .getTime();
232: }
233:
234: public void addChangeListener(final ChangeListener l) {
235: synchronized (listeners) {
236: listeners.add(l);
237: }
238: }
239:
240: public void removeChangeListener(final ChangeListener l) {
241: synchronized (listeners) {
242: listeners.remove(l);
243: }
244: }
245:
246: private void fireChange() {
247: ChangeListener[] _listeners;
248: synchronized (listeners) {
249: if (listeners.isEmpty()) {
250: return;
251: }
252: _listeners = listeners
253: .toArray(new ChangeListener[listeners.size()]);
254: }
255: final ChangeEvent ev = new ChangeEvent(this );
256: for (int i = 0; i < _listeners.length; i++) {
257: _listeners[i].stateChanged(ev);
258: }
259: }
260:
261: private void update() {
262: RequestProcessor.getDefault().post(StatusImpl.this );
263: }
264:
265: public void run() {
266: isBuilt();
267: }
268:
269: public void propertyChange(final PropertyChangeEvent evt) {
270: assert evt.getSource() instanceof DataObject;
271: if (DataObject.PROP_MODIFIED.equals(evt.getPropertyName())) {
272: update();
273: }
274: }
275:
276: public void fileChanged(@SuppressWarnings("unused")
277: final FileEvent fe) {
278: update();
279: }
280:
281: public void fileDeleted(@SuppressWarnings("unused")
282: final FileEvent fe) {
283: update();
284: }
285:
286: public void fileRenamed(@SuppressWarnings("unused")
287: final FileRenameEvent fe) {
288: update();
289: }
290:
291: public void fileDataCreated(@SuppressWarnings("unused")
292: final FileEvent fe) {
293: update();
294: }
295:
296: public void fileFolderCreated(@SuppressWarnings("unused")
297: final FileEvent fe) {
298: update();
299: }
300:
301: public void fileAttributeChanged(@SuppressWarnings("unused")
302: final FileAttributeEvent fe) {
303: // ignore
304: }
305:
306: public String toString() {
307: return "FBQI.StatusImpl[" + source.getPrimaryFile()
308: + " -> " + getTarget() + "]"; // NOI18N
309: }
310: }
311: }
|