|
1 /* |
|
2 * Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 package com.nokia.carbide.internal.cpp.epoc.engine.model; |
|
19 |
|
20 import com.nokia.carbide.cpp.epoc.engine.DocumentFactory; |
|
21 import com.nokia.carbide.cpp.epoc.engine.EpocEnginePlugin; |
|
22 import com.nokia.carbide.cpp.epoc.engine.model.IModel; |
|
23 import com.nokia.carbide.cpp.epoc.engine.model.IModelFactory; |
|
24 import com.nokia.carbide.cpp.epoc.engine.model.IModelProvider; |
|
25 import com.nokia.carbide.cpp.epoc.engine.model.IOwnedModel; |
|
26 import com.nokia.cpp.internal.api.utils.core.*; |
|
27 |
|
28 import org.eclipse.core.runtime.CoreException; |
|
29 import org.eclipse.core.runtime.IPath; |
|
30 import org.eclipse.jface.text.IDocument; |
|
31 |
|
32 import java.util.Collection; |
|
33 import java.util.HashMap; |
|
34 import java.util.HashSet; |
|
35 import java.util.Iterator; |
|
36 import java.util.Map; |
|
37 import java.util.Set; |
|
38 |
|
39 /** |
|
40 * Base implementation of a model provider. Real implementations |
|
41 * need to provide a mechanism for retrieving and saving file contents. |
|
42 * |
|
43 */ |
|
44 public abstract class ModelProviderBase implements IModelProvider { |
|
45 |
|
46 // not static or final so it can change during test |
|
47 private boolean DUMP = false; |
|
48 |
|
49 private static int gProviderCounter; |
|
50 private int providerId; |
|
51 |
|
52 private static final long RELEASE_DELAY = 5 * 1000; |
|
53 private boolean cacheModels; |
|
54 |
|
55 // this acts as the lock on all the state managed by this object |
|
56 private Object modelLock = new Object(); |
|
57 |
|
58 private Map<IPath, IOwnedModel> models; |
|
59 //private Map<IPath, IModelListener> modelListeners; |
|
60 private Map<IPath, Integer> modelUseCount; |
|
61 private Map<IPath, Long> releasedModelTimes; |
|
62 private Map<IPath, IOwnedModel> releasedModels; |
|
63 private Map<IPath, Set<IOwnedModel>> modelFileReferences; |
|
64 protected IModelFactory modelFactory; |
|
65 |
|
66 private long lastFlushTime; |
|
67 |
|
68 public ModelProviderBase(IModelFactory modelFactory) { |
|
69 providerId = ++gProviderCounter; |
|
70 Check.checkArg(modelFactory); |
|
71 this.cacheModels = true; |
|
72 this.modelFactory = modelFactory; |
|
73 this.modelUseCount = new HashMap<IPath, Integer>(); |
|
74 this.models = new HashMap<IPath, IOwnedModel>(); |
|
75 //this.modelListeners = new HashMap<IPath, IModelListener>(); |
|
76 this.releasedModelTimes = new HashMap<IPath, Long>(); |
|
77 this.releasedModels = new HashMap<IPath, IOwnedModel>(); |
|
78 this.modelFileReferences = new HashMap<IPath, Set<IOwnedModel>>(); |
|
79 } |
|
80 |
|
81 /** |
|
82 * Get the canonical form of the path -- a full filesystem path. |
|
83 */ |
|
84 protected abstract IPath getFullPath(IPath path); |
|
85 |
|
86 /** |
|
87 * Load the text from the given full path, or return null |
|
88 * for a nonexistent file. |
|
89 * @param fullPath full path to resource |
|
90 * @return contents of file |
|
91 * @throws CoreException if the file could not be read, |
|
92 * for reasons other than not existing. |
|
93 */ |
|
94 protected abstract String loadStorage(IPath fullPath) throws CoreException; |
|
95 |
|
96 /** |
|
97 * Save the text to the given full path, creating it and its parents if |
|
98 * necessary. The implementation may ignore a save if the text does not differ. |
|
99 * @param path full path to resource |
|
100 * @param text the text |
|
101 */ |
|
102 protected abstract void saveStorage(IPath fullPath, String text) throws CoreException; |
|
103 |
|
104 /** |
|
105 * Enable tracking for the storage underlying a registered model's contents. |
|
106 * This may be called multiple times for an already-registered path. |
|
107 * <p> |
|
108 * If changes are detected, {@link #fireStorageChanged()} should be called. |
|
109 * <p> |
|
110 * A call to {@link #stopTrackingStorage(IPath)} is made around approved |
|
111 * saves of documents, so implementors don't have to handle that. |
|
112 */ |
|
113 protected abstract void startTrackingStorage(IPath fullPath); |
|
114 |
|
115 /** |
|
116 * Disable tracking for the storage underlying a registered model's contents. |
|
117 */ |
|
118 protected abstract void stopTrackingStorage(IPath fullPath); |
|
119 |
|
120 /** |
|
121 * Called by implementors to notify that the storage at the given |
|
122 * path has changed. This is called by the implementation |
|
123 * when changes to tracked storage can be detected with listeners. |
|
124 * @param path |
|
125 * @see #startTrackingStorage(IPath) |
|
126 * @see #stopTrackingStorage(IPath) |
|
127 */ |
|
128 protected void fireStorageChanged(IPath fullPath) { |
|
129 IOwnedModel model = lookupSharedModel(fullPath); |
|
130 if (model != null) { |
|
131 // the model document itself was modified |
|
132 refreshModel(fullPath, model); |
|
133 } else { |
|
134 // see if a header for one of the models was modified |
|
135 Collection<IOwnedModel> referencingModels = modelFileReferences.get(fullPath); |
|
136 if (referencingModels != null) { |
|
137 for (IOwnedModel refModel : referencingModels) { |
|
138 refreshModel(refModel.getPath(), refModel); |
|
139 } |
|
140 } |
|
141 } |
|
142 } |
|
143 |
|
144 /** |
|
145 * Check the model's file for changes. This acts as a polling solution for cases |
|
146 * where changes to tracked storage cannot be implemented with listeners. |
|
147 * <p> |
|
148 * The implementation need only store modification information when files are |
|
149 * loaded and saved and double-check that information here. This may return false |
|
150 * if the implementation reports changes with {@link #fireStorageChanged(IPath)}. |
|
151 * <p> |
|
152 * The implementation must not call {@link #fireStorageChanged(IPath)}. |
|
153 * @see #startTrackingStorage(IPath) |
|
154 * @see #stopTrackingStorage(IPath) |
|
155 */ |
|
156 protected abstract boolean hasTrackedStorageChanged(IPath fullPath); |
|
157 |
|
158 public void updateModelDocumentMappings(IOwnedModel model) { |
|
159 startTrackingModelStorage(model); |
|
160 } |
|
161 |
|
162 /** |
|
163 * Start tracking storage for all the documents owned by the model. |
|
164 * @param model |
|
165 */ |
|
166 private void startTrackingModelStorage(IOwnedModel model) { |
|
167 Map<IPath, IDocument> documentMap = model.getDocumentMap(); |
|
168 for (IPath path : documentMap.keySet()) { |
|
169 Set<IOwnedModel> models = modelFileReferences.get(path); |
|
170 if (models == null) { |
|
171 models = new HashSet<IOwnedModel>(); |
|
172 modelFileReferences.put(path, models); |
|
173 } |
|
174 models.add(model); |
|
175 startTrackingStorage(path); |
|
176 } |
|
177 } |
|
178 |
|
179 /** |
|
180 * Start tracking storage for all the documents owned by the model. |
|
181 * @param model |
|
182 */ |
|
183 private void stopTrackingModelStorage(IOwnedModel model) { |
|
184 Map<IPath, IDocument> documentMap = model.getDocumentMap(); |
|
185 for (IPath path : documentMap.keySet()) { |
|
186 Set<IOwnedModel> models = modelFileReferences.get(path); |
|
187 if (models != null) { |
|
188 models.remove(model); |
|
189 if (models.isEmpty()) { |
|
190 modelFileReferences.remove(path); |
|
191 } |
|
192 } |
|
193 stopTrackingStorage(path); |
|
194 } |
|
195 } |
|
196 |
|
197 /** |
|
198 * Refresh the model's contents from storage, if changed. |
|
199 * @param fullPath |
|
200 * @param model |
|
201 */ |
|
202 private void refreshModel(IPath fullPath, IOwnedModel model) { |
|
203 try { |
|
204 // get updated file |
|
205 String text = loadStorage(fullPath); |
|
206 |
|
207 // make model reload with new contents |
|
208 String currentText = model.getDocument().get(); |
|
209 if (currentText == null || !currentText.equals(text)) { |
|
210 model.getDocument().set(text != null ? text : ""); //$NON-NLS-1$ |
|
211 |
|
212 // the change to the document should trigger a reparse |
|
213 // and invalidation of any open views |
|
214 } |
|
215 |
|
216 } catch (CoreException e) { |
|
217 EpocEnginePlugin.log(e); |
|
218 } |
|
219 } |
|
220 |
|
221 /* (non-Javadoc) |
|
222 * @see com.nokia.carbide.cpp.epoc.engine.model.IModelProvider#createModel(org.eclipse.core.runtime.IPath) |
|
223 */ |
|
224 public IOwnedModel createModel(IPath somePath) { |
|
225 IPath fullPath = getFullPath(somePath); |
|
226 return modelFactory.createModel(fullPath, null); |
|
227 } |
|
228 |
|
229 /* (non-Javadoc) |
|
230 * @see com.nokia.carbide.cpp.epoc.engine.model.IModelProvider#load(com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPOwnedModel) |
|
231 */ |
|
232 public void load(IOwnedModel model) throws CoreException { |
|
233 String text = loadStorage(model.getPath()); |
|
234 IDocument document = model.getDocument() != null ? model.getDocument() : DocumentFactory.createDocument(); |
|
235 if (text != null) |
|
236 document.set(text); |
|
237 if (document != model.getDocument()) { |
|
238 model.setDocument(document); |
|
239 } |
|
240 } |
|
241 |
|
242 /* (non-Javadoc) |
|
243 * @see com.nokia.carbide.cpp.epoc.engine.model.IModelProvider#save(com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPOwnedModel) |
|
244 */ |
|
245 public void save(IOwnedModel model) throws CoreException { |
|
246 save(model, model.getDocumentMap()); |
|
247 } |
|
248 |
|
249 /* (non-Javadoc) |
|
250 * @see com.nokia.carbide.cpp.epoc.engine.model.IModelProvider#save(com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPOwnedModel) |
|
251 */ |
|
252 public void save(IOwnedModel model, Map documentMap_) throws CoreException { |
|
253 Map<IPath, IDocument> documentMap = documentMap_; |
|
254 |
|
255 IPath path = model.getPath(); |
|
256 |
|
257 if (models.containsKey(path)) { |
|
258 stopTrackingModelStorage(model); |
|
259 } |
|
260 |
|
261 // check for changed documents and save modified files only |
|
262 for (Map.Entry<IPath, IDocument> entry : documentMap.entrySet()) { |
|
263 saveStorage(entry.getKey(), entry.getValue().get()); |
|
264 } |
|
265 |
|
266 if (models.containsKey(path)) { |
|
267 startTrackingModelStorage(model); |
|
268 } |
|
269 } |
|
270 |
|
271 /** |
|
272 * Register a model whose document has been established. |
|
273 * Called with modelLock held. |
|
274 * @param model |
|
275 * @throws CoreException |
|
276 */ |
|
277 private void doRegisterModel(IOwnedModel model) throws CoreException { |
|
278 IPath path = model.getPath(); |
|
279 Check.checkArg(!models.containsKey(path)); |
|
280 if (DUMP) System.out.println(providerId+") " + "Registering "+ path); //$NON-NLS-1$ //$NON-NLS-2$ |
|
281 if (releasedModels.containsKey(path)) { |
|
282 if (DUMP) System.out.println("--> registering released model"); //$NON-NLS-1$ |
|
283 releasedModels.remove(path); |
|
284 releasedModelTimes.remove(path); |
|
285 } |
|
286 models.put(path, model); |
|
287 model.setModelProvider(this); |
|
288 |
|
289 /* |
|
290 IModelListener modelListener = new IModelListener() { |
|
291 |
|
292 public void modelChanged(IOwnedModel model) { |
|
293 } |
|
294 |
|
295 public void modelUpdated(IOwnedModel model, IView view) { |
|
296 try { |
|
297 save(model); |
|
298 } catch (CoreException e) { |
|
299 EpocEnginePlugin.log(e); |
|
300 } |
|
301 } |
|
302 }; |
|
303 |
|
304 model.addListener(modelListener); |
|
305 modelListeners.put(path, modelListener); |
|
306 */ |
|
307 |
|
308 // owner holds one reference |
|
309 modelUseCount.put(path, new Integer(1)); |
|
310 |
|
311 // ensure the model is fully set up |
|
312 model.parse(); |
|
313 startTrackingModelStorage(model); |
|
314 } |
|
315 |
|
316 /* (non-Javadoc) |
|
317 * @see com.nokia.carbide.cpp.epoc.engine.model.IModelProvider#registerModel(SharedModel) |
|
318 */ |
|
319 public void registerModel(IOwnedModel model) throws CoreException { |
|
320 synchronized (modelLock) { |
|
321 if (model.getDocument() != null) { |
|
322 // create from initial contents |
|
323 save(model); |
|
324 } else { |
|
325 // try to load, or make empty if not existing |
|
326 load(model); |
|
327 } |
|
328 |
|
329 doRegisterModel(model); |
|
330 } |
|
331 } |
|
332 |
|
333 /* (non-Javadoc) |
|
334 * @see com.nokia.carbide.cpp.epoc.engine.model.IModelProvider#unregisterModel(SharedModel) |
|
335 */ |
|
336 public void unregisterModel(IOwnedModel model) { |
|
337 IPath path = model.getPath(); |
|
338 synchronized (modelLock) { |
|
339 if (DUMP) System.out.println(providerId+") " + "Unregistering " + path); //$NON-NLS-1$ //$NON-NLS-2$ |
|
340 Check.checkArg(models.containsKey(path) || releasedModels.containsKey(path)); |
|
341 //model.removeListener(modelListeners.get(path)); |
|
342 //modelListeners.remove(path); |
|
343 models.remove(path); |
|
344 releasedModels.remove(path); |
|
345 releasedModelTimes.remove(path); |
|
346 model.setModelProvider(null); |
|
347 } |
|
348 stopTrackingModelStorage(model); |
|
349 } |
|
350 |
|
351 private IOwnedModel lookupSharedModel(IPath fullPath) { |
|
352 IOwnedModel model; |
|
353 if (DUMP) System.out.println(providerId+") " + "Finding shared model " + fullPath); //$NON-NLS-1$ //$NON-NLS-2$ |
|
354 // try to resurrect released model |
|
355 model = releasedModels.get(fullPath); |
|
356 if (model != null) { |
|
357 if (DUMP) System.out.println("--> resurrecting"); //$NON-NLS-1$ |
|
358 models.put(fullPath, model); |
|
359 releasedModels.remove(fullPath); |
|
360 releasedModelTimes.remove(fullPath); |
|
361 } else { |
|
362 if (DUMP) System.out.println("--> looking up"); //$NON-NLS-1$ |
|
363 model = models.get(fullPath); |
|
364 } |
|
365 if (model != null) { |
|
366 incrementUseCount(fullPath); |
|
367 if (DUMP) System.out.println("--> found, refcount = " + testGetUseCount(model)); //$NON-NLS-1$ |
|
368 } |
|
369 return model; |
|
370 } |
|
371 |
|
372 public IModel findSharedModel(IPath somePath) { |
|
373 if (somePath == null) { |
|
374 return null; |
|
375 } |
|
376 IPath fullPath = getFullPath(somePath); |
|
377 synchronized (modelLock) { |
|
378 IOwnedModel model = lookupSharedModel(fullPath); |
|
379 if (model != null && hasTrackedStorageChanged(fullPath)) { |
|
380 refreshModel(fullPath, model); |
|
381 } |
|
382 return model; |
|
383 } |
|
384 } |
|
385 |
|
386 /* (non-Javadoc) |
|
387 * @see com.nokia.carbide.cpp.epoc.engine.model.IModelProvider#getSharedModel(org.eclipse.core.runtime.IPath, com.nokia.carbide.cpp.epoc.engine.model.IModelConfiguration) |
|
388 */ |
|
389 public IModel getSharedModel(IPath somePath) throws CoreException { |
|
390 if (somePath == null) { |
|
391 throw new CoreException(Logging.newStatus(EpocEnginePlugin.getDefault(), |
|
392 new NullPointerException("Model path is null... perhaps outside workspace?"))); //$NON-NLS-1$ |
|
393 } |
|
394 IPath fullPath = getFullPath(somePath); |
|
395 IOwnedModel model; |
|
396 synchronized (modelLock) { |
|
397 if (DUMP) System.out.println(providerId+") " + "Get shared " + fullPath); //$NON-NLS-1$ //$NON-NLS-2$ |
|
398 model = releasedModels.get(fullPath); |
|
399 if (model != null) { |
|
400 if (DUMP) System.out.println("--> resurrect"); //$NON-NLS-1$ |
|
401 Check.checkState(modelUseCount.get(fullPath).equals(0)); |
|
402 models.put(fullPath, model); |
|
403 releasedModels.remove(fullPath); |
|
404 releasedModelTimes.remove(fullPath); |
|
405 } else { |
|
406 if (DUMP) System.out.println("--> lookup"); //$NON-NLS-1$ |
|
407 model = models.get(fullPath); |
|
408 } |
|
409 if (model == null) { |
|
410 if (DUMP) System.out.println("--> load"); //$NON-NLS-1$ |
|
411 |
|
412 // try to load |
|
413 String text = loadStorage(fullPath); |
|
414 if (text == null) |
|
415 return null; |
|
416 |
|
417 if (DUMP) System.out.print("--> loading model " + fullPath + "... "); //$NON-NLS-1$ //$NON-NLS-2$ |
|
418 |
|
419 model = modelFactory.createModel(fullPath, DocumentFactory.createDocument(text)); |
|
420 doRegisterModel(model); |
|
421 if (DUMP) System.out.println("loaded."); //$NON-NLS-1$ |
|
422 } else { |
|
423 // check for changes under our nose |
|
424 if (hasTrackedStorageChanged(fullPath)) { |
|
425 refreshModel(fullPath, model); |
|
426 } |
|
427 |
|
428 // ensure the model is tracked now, if it wasn't before (e.g. because it was not yet in the workspace) |
|
429 startTrackingModelStorage(model); |
|
430 |
|
431 incrementUseCount(fullPath); |
|
432 if (DUMP) System.out.println("--> incr refcount to " + testGetUseCount(model)); //$NON-NLS-1$ |
|
433 } |
|
434 } |
|
435 |
|
436 // try cleaning up (this could be called as a job too) |
|
437 flushReleasedModels(); |
|
438 |
|
439 return model; |
|
440 } |
|
441 |
|
442 /** |
|
443 * TESTING ONLY. |
|
444 * <p> |
|
445 * @param model |
|
446 * @return |
|
447 */ |
|
448 public int testGetUseCount(IOwnedModel model) { |
|
449 return modelUseCount.get(model.getPath()); |
|
450 } |
|
451 |
|
452 /** |
|
453 * Increment use count for the given model.<p> |
|
454 * Called with #modelLock held. |
|
455 * @param modelPath |
|
456 */ |
|
457 private void incrementUseCount(IPath modelPath) { |
|
458 Integer val = modelUseCount.get(modelPath); |
|
459 modelUseCount.put(modelPath, new Integer(val.intValue() + 1)); |
|
460 } |
|
461 |
|
462 /** |
|
463 * Decrement use count for the given model.<p> |
|
464 * Called with #modelLock held. |
|
465 * @param modelPath |
|
466 */ |
|
467 private int decrementUseCount(IPath modelPath) { |
|
468 Integer val = modelUseCount.get(modelPath); |
|
469 int newValue = val.intValue() - 1; |
|
470 Check.checkState(newValue >= 0); |
|
471 modelUseCount.put(modelPath, new Integer(newValue)); |
|
472 return newValue; |
|
473 } |
|
474 |
|
475 /* (non-Javadoc) |
|
476 * @see com.nokia.carbide.cpp.epoc.engine.model.IModelProvider#releaseSharedModel(SharedModel) |
|
477 */ |
|
478 public void releaseSharedModel(IModel sharedModel) { |
|
479 IOwnedModel model = (IOwnedModel) sharedModel; |
|
480 IPath path = model.getPath(); |
|
481 synchronized (modelLock) { |
|
482 if (DUMP) System.out.println(providerId+") " + "Releasing " + path); //$NON-NLS-1$ //$NON-NLS-2$ |
|
483 Check.checkState(models.containsKey(path)); |
|
484 int val = decrementUseCount(path); |
|
485 if (val == 0) { |
|
486 if (sharedModel.getViews().length != 0) { |
|
487 incrementUseCount(path); |
|
488 throw new IllegalStateException("Trying to release model with views: " + path); //$NON-NLS-1$ |
|
489 } |
|
490 if (DUMP) System.out.println("--> refcount == 0"); //$NON-NLS-1$ |
|
491 models.remove(path); |
|
492 //modelListeners.remove(path); |
|
493 if (cacheModels) { |
|
494 if (DUMP) System.out.println("--> released"); //$NON-NLS-1$ |
|
495 // move to released models cache |
|
496 releasedModels.put(path, model); |
|
497 releasedModelTimes.put(path, System.currentTimeMillis()); |
|
498 } else { |
|
499 if (DUMP) System.out.println("--> disposing"); //$NON-NLS-1$ |
|
500 model.setModelProvider(null); |
|
501 model.dispose(); |
|
502 modelUseCount.remove(path); |
|
503 } |
|
504 } else { |
|
505 if (DUMP) System.out.println("--> refcount == " + val); //$NON-NLS-1$ |
|
506 } |
|
507 } |
|
508 } |
|
509 |
|
510 |
|
511 /** |
|
512 * Testing method to clear out the provider's contents. |
|
513 */ |
|
514 public void reset() { |
|
515 synchronized (modelLock) { |
|
516 if (DUMP) System.out.println(providerId+") " + "Reset!"); //$NON-NLS-1$ //$NON-NLS-2$ |
|
517 models.clear(); |
|
518 //modelListeners.clear(); |
|
519 modelUseCount.clear(); |
|
520 releasedModelTimes.clear(); |
|
521 releasedModels.clear(); |
|
522 } |
|
523 } |
|
524 |
|
525 /** |
|
526 * Actually flush a model. This is done some time after the last |
|
527 * #release is called. |
|
528 * <p> |
|
529 * Called with #modelLock held. |
|
530 * @param model |
|
531 * @param path |
|
532 */ |
|
533 private void flushModel(IPath modelPath) { |
|
534 if (!releasedModels.containsKey(modelPath)) { |
|
535 EpocEnginePlugin.log(new IllegalArgumentException("Flushing unreleased model? " + modelPath)); //$NON-NLS-1$ |
|
536 } |
|
537 try { |
|
538 IOwnedModel model = releasedModels.get(modelPath); |
|
539 model.setModelProvider(null); |
|
540 model.dispose(); |
|
541 } catch (IllegalStateException e) { |
|
542 throw e; |
|
543 } |
|
544 releasedModels.remove(modelPath); |
|
545 releasedModelTimes.remove(modelPath); |
|
546 modelUseCount.remove(modelPath); |
|
547 } |
|
548 |
|
549 /** |
|
550 * Occasionally scan released models and free them, |
|
551 * using a delay to avoid overhead of rescanning popular models. |
|
552 */ |
|
553 private void flushReleasedModels() { |
|
554 long now = System.currentTimeMillis(); |
|
555 if (lastFlushTime + RELEASE_DELAY > now) { |
|
556 return; |
|
557 } |
|
558 synchronized (modelLock) { |
|
559 if (DUMP) System.out.println(providerId+") " + "Flush models... "); //$NON-NLS-1$ //$NON-NLS-2$ |
|
560 |
|
561 // work on copy so #flushModel can fix up everything |
|
562 // (else we fix up the time map here, which is not symmetric) |
|
563 Map<IPath, Long> timeMap = new HashMap<IPath, Long>(releasedModelTimes); |
|
564 for (Iterator<Map.Entry<IPath, Long>> iter = timeMap.entrySet().iterator(); iter.hasNext(); ) { |
|
565 Map.Entry<IPath, Long> entry = iter.next(); |
|
566 if (entry.getValue() + RELEASE_DELAY < now) { |
|
567 if (DUMP) System.out.println("Flushing model " + entry.getKey()); //$NON-NLS-1$ |
|
568 flushModel(entry.getKey()); |
|
569 } |
|
570 } |
|
571 if (DUMP) System.out.println("flushed."); //$NON-NLS-1$ |
|
572 } |
|
573 lastFlushTime = now; |
|
574 } |
|
575 |
|
576 public void cacheModels(boolean flag) { |
|
577 this.cacheModels = flag; |
|
578 } |
|
579 |
|
580 public boolean isCachingModels() { |
|
581 return cacheModels; |
|
582 } |
|
583 |
|
584 } |