1 19 20 package org.netbeans.spi.project.support.ant; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.io.File ; 25 import java.lang.ref.Reference ; 26 import java.lang.ref.WeakReference ; 27 import java.util.ArrayList ; 28 import java.util.Arrays ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.WeakHashMap ; 32 import java.util.logging.Level ; 33 import java.util.logging.Logger ; 34 import javax.swing.event.ChangeEvent ; 35 import javax.swing.event.ChangeListener ; 36 import org.netbeans.api.queries.FileBuiltQuery; 37 import org.netbeans.modules.project.ant.FileChangeSupport; 38 import org.netbeans.modules.project.ant.FileChangeSupportEvent; 39 import org.netbeans.modules.project.ant.FileChangeSupportListener; 40 import org.netbeans.spi.queries.FileBuiltQueryImplementation; 41 import org.openide.ErrorManager; 42 import org.openide.filesystems.FileAttributeEvent; 43 import org.openide.filesystems.FileChangeListener; 44 import org.openide.filesystems.FileEvent; 45 import org.openide.filesystems.FileObject; 46 import org.openide.filesystems.FileRenameEvent; 47 import org.openide.filesystems.FileUtil; 48 import org.openide.loaders.DataObject; 49 import org.openide.loaders.DataObjectNotFoundException; 50 import org.openide.util.Exceptions; 51 import org.openide.util.RequestProcessor; 52 import org.openide.util.Utilities; 53 import org.openide.util.WeakListeners; 54 55 60 final class GlobFileBuiltQuery implements FileBuiltQueryImplementation { 61 62 private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.netbeans.spi.project.support.ant.GlobFileBuiltQuery"); 64 private final AntProjectHelper helper; 65 private final PropertyEvaluator eval; 66 private final String [] fromPrefixes; 67 private final String [] fromSuffixes; 68 private final String [] toPrefixes; 69 private final String [] toSuffixes; 70 private static final Reference <StatusImpl> NONE = new WeakReference <StatusImpl>(null); 71 private final Map <FileObject,Reference <StatusImpl>> statuses = new WeakHashMap <FileObject,Reference <StatusImpl>>(); 72 73 77 public GlobFileBuiltQuery(AntProjectHelper helper, PropertyEvaluator eval, String [] from, String [] to) throws IllegalArgumentException { 78 this.helper = helper; 79 this.eval = eval; 80 int l = from.length; 81 if (to.length != l) { 82 throw new IllegalArgumentException ("Non-matching lengths"); } 84 fromPrefixes = new String [l]; 85 fromSuffixes = new String [l]; 86 toPrefixes = new String [l]; 87 toSuffixes = new String [l]; 88 for (int i = 0; i < l; i++) { 89 int idx = from[i].indexOf('*'); 90 if (idx == -1 || idx != from[i].lastIndexOf('*')) { 91 throw new IllegalArgumentException ("Zero or multiple asterisks in " + from[i]); } 93 fromPrefixes[i] = from[i].substring(0, idx); 94 fromSuffixes[i] = from[i].substring(idx + 1); 95 idx = to[i].indexOf('*'); 96 if (idx == -1 || idx != to[i].lastIndexOf('*')) { 97 throw new IllegalArgumentException ("Zero or multiple asterisks in " + to[i]); } 99 toPrefixes[i] = to[i].substring(0, idx); 100 toSuffixes[i] = to[i].substring(idx + 1); 101 } 104 } 108 109 public synchronized FileBuiltQuery.Status getStatus(FileObject file) { 110 Reference <StatusImpl> r = statuses.get(file); 111 if (r == NONE) { 112 return null; 113 } 114 StatusImpl status = (r != null) ? r.get() : null; 115 if (status == null) { 116 status = createStatus(file); 117 if (status != null) { 118 statuses.put(file, new WeakReference <StatusImpl>(status)); 119 } else { 120 statuses.put(file, NONE); 121 } 122 } 123 return status; 124 } 125 126 private File findTarget(FileObject file) { 127 File sourceF = FileUtil.toFile(file); 128 if (sourceF == null) { 129 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 130 err.log("Not a disk file: " + file); 131 } 132 return null; 133 } 134 String source = sourceF.getAbsolutePath(); 135 for (int i = 0; i < fromPrefixes.length; i++) { 136 String prefixEval = eval.evaluate(fromPrefixes[i]); 137 if (prefixEval == null) { 138 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 139 err.log(fromPrefixes[i] + " evaluates to null"); 140 } 141 continue; 142 } 143 String suffixEval = eval.evaluate(fromSuffixes[i]); 144 if (suffixEval == null) { 145 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 146 err.log(fromSuffixes[i] + " evaluates to null"); 147 } 148 continue; 149 } 150 boolean endsWithSlash = prefixEval.endsWith("/"); String prefixF = helper.resolveFile(prefixEval).getAbsolutePath(); 152 if (endsWithSlash && !prefixF.endsWith(File.separator)) { 153 prefixF += File.separatorChar; 154 } 155 if (!source.startsWith(prefixF)) { 156 continue; 157 } 158 String remainder = source.substring(prefixF.length()); 159 if (!remainder.endsWith(suffixEval.replace('/', File.separatorChar))) { 160 continue; 161 } 162 String particular = remainder.substring(0, remainder.length() - suffixEval.length()); 163 String toPrefixEval = eval.evaluate(toPrefixes[i]); 164 if (toPrefixEval == null) { 165 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 166 err.log(toPrefixes[i] + " evaluates to null"); 167 } 168 continue; 169 } 170 String toSuffixEval = eval.evaluate(toSuffixes[i]); 171 if (toSuffixEval == null) { 172 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 173 err.log(toSuffixes[i] + " evaluates to null"); 174 } 175 continue; 176 } 177 File target = helper.resolveFile(toPrefixEval + particular + toSuffixEval); 178 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 179 err.log("Found target for " + source + ": " + target); 180 } 181 return target; 182 } 183 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 184 err.log("No match for path " + source + " among " + Arrays.asList(fromPrefixes) + " " + Arrays.asList(fromSuffixes)); 185 } 186 return null; 187 } 188 189 private StatusImpl createStatus(FileObject file) { 190 File target = findTarget(file); 191 if (target != null) { 192 try { 193 DataObject source = DataObject.find(file); 194 195 return new StatusImpl(source, file, target); 196 } catch (DataObjectNotFoundException e) { 197 Logger.getLogger(GlobFileBuiltQuery.class.getName()).log(Level.FINE, null, e); 198 return null; 199 } 200 } else { 201 return null; 202 } 203 } 204 205 private final class StatusImpl implements FileBuiltQuery.Status, PropertyChangeListener , FileChangeListener, FileChangeSupportListener, Runnable { 206 207 private final List <ChangeListener > listeners = new ArrayList <ChangeListener >(); 208 private Boolean built = null; 209 private final DataObject source; 210 private File target; 211 212 StatusImpl(DataObject source, FileObject sourceFO, File target) { 213 this.source = source; 214 this.source.addPropertyChangeListener(WeakListeners.propertyChange(this, this.source)); 215 sourceFO.addFileChangeListener(FileUtil.weakFileChangeListener(this, sourceFO)); 216 this.target = target; 217 FileChangeSupport.DEFAULT.addListener(this, target); 218 } 219 220 public boolean isBuilt() { 222 boolean doFire = false; 223 boolean b; 224 synchronized (GlobFileBuiltQuery.this) { 225 b = isReallyBuilt(); 226 if (built != null && built.booleanValue() != b) { 227 doFire = true; 228 } 229 built = Boolean.valueOf(b); 230 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 231 err.log("isBuilt: " + b + " from " + this); 232 } 233 } 234 if (doFire) { 235 fireChange(); 236 } 237 return b; 238 } 239 240 private boolean isReallyBuilt() { 241 if (!source.isValid()) { 242 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 243 err.log("invalid: " + this); 244 } 245 return false; } 247 if (source.isModified()) { 248 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 249 err.log("modified: " + this); 250 } 251 return false; 252 } 253 if (target == null) { 254 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 255 err.log("no target matching " + this); 256 } 257 return false; 258 } 259 long targetTime = target.lastModified(); 260 long sourceTime = source.getPrimaryFile().lastModified().getTime(); 261 if (targetTime >= sourceTime) { 262 return true; 263 } else { 264 if (err.isLoggable(ErrorManager.INFORMATIONAL)) { 265 err.log("out of date (target: " + targetTime + " vs. source: " + sourceTime + "): " + this); 266 } 267 return false; 268 } 269 } 270 271 public void addChangeListener(ChangeListener l) { 272 synchronized (listeners) { 273 listeners.add(l); 274 } 275 } 276 277 public void removeChangeListener(ChangeListener l) { 278 synchronized (listeners) { 279 listeners.remove(l); 280 } 281 } 282 283 private void fireChange() { 284 ChangeListener [] _listeners; 285 synchronized (listeners) { 286 if (listeners.isEmpty()) { 287 return; 288 } 289 _listeners = listeners.toArray(new ChangeListener [listeners.size()]); 290 } 291 ChangeEvent ev = new ChangeEvent (this); 292 for (ChangeListener l : _listeners) { 293 l.stateChanged(ev); 294 } 295 } 296 297 private void update() { 298 RequestProcessor.getDefault().post(this); 299 } 300 301 public void run() { 302 isBuilt(); 303 } 304 305 public void propertyChange(PropertyChangeEvent evt) { 306 assert evt.getSource() instanceof DataObject; 307 if (DataObject.PROP_MODIFIED.equals(evt.getPropertyName())) { 308 update(); 309 } 310 } 311 312 public void fileChanged(FileEvent fe) { 313 update(); 314 } 315 316 public void fileDeleted(FileEvent fe) { 317 update(); 318 } 319 320 public void fileRenamed(FileRenameEvent fe) { 321 File target2 = findTarget(source.getPrimaryFile()); 322 if (!Utilities.compareObjects(target, target2)) { 323 if (target != null) { 325 FileChangeSupport.DEFAULT.removeListener(this, target); 326 } 327 if (target2 != null) { 328 FileChangeSupport.DEFAULT.addListener(this, target2); 329 } 330 target = target2; 331 } 332 update(); 333 } 334 335 public void fileDataCreated(FileEvent fe) { 336 } 338 339 public void fileFolderCreated(FileEvent fe) { 340 } 342 343 public void fileAttributeChanged(FileAttributeEvent fe) { 344 } 346 347 public void fileCreated(FileChangeSupportEvent event) { 348 update(); 349 } 350 351 public void fileDeleted(FileChangeSupportEvent event) { 352 update(); 353 } 354 355 public void fileModified(FileChangeSupportEvent event) { 356 update(); 357 } 358 359 public String toString() { 360 return "GFBQ.StatusImpl[" + source.getPrimaryFile() + " -> " + target + "]"; } 362 363 } 364 365 } 366 | Popular Tags |