001 /*
002 * Copyright (c) 2004, 2009 IBM Corporation, Embarcadero Technologies, and others.
003 * All rights reserved. This program and the accompanying materials
004 * are made available under the terms of the Eclipse Public License v1.0
005 * which accompanies this distribution, and is available at
006 * http://www.eclipse.org/legal/epl-v10.html
007 *
008 * Contributors:
009 * IBM - initial API and implementation
010 * Kenn Hussey (Embarcadero Technologies) - 204200, 220065
011 *
012 * $Id: CacheAdapter.java,v 1.26 2009/03/13 20:41:16 jbruck Exp $
013 */
014 package org.eclipse.uml2.common.util;
015
016 import java.util.Collection;
017 import java.util.Collections;
018 import java.util.HashMap;
019 import java.util.Iterator;
020 import java.util.List;
021 import java.util.Map;
022 import java.util.Set;
023 import java.util.WeakHashMap;
024
025 import org.eclipse.emf.common.notify.Adapter;
026 import org.eclipse.emf.common.notify.Notification;
027 import org.eclipse.emf.common.notify.Notifier;
028 import org.eclipse.emf.common.util.BasicEList;
029 import org.eclipse.emf.common.util.EList;
030 import org.eclipse.emf.common.util.URI;
031 import org.eclipse.emf.ecore.EObject;
032 import org.eclipse.emf.ecore.EReference;
033 import org.eclipse.emf.ecore.EStructuralFeature;
034 import org.eclipse.emf.ecore.InternalEObject;
035 import org.eclipse.emf.ecore.resource.Resource;
036 import org.eclipse.emf.ecore.resource.ResourceSet;
037 import org.eclipse.emf.ecore.resource.URIConverter;
038 import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
039 import org.eclipse.emf.ecore.util.EcoreUtil;
040
041 public class CacheAdapter
042 extends ECrossReferenceAdapter {
043
044 protected class InverseCrossReferencer
045 extends ECrossReferenceAdapter.InverseCrossReferencer {
046
047 private static final long serialVersionUID = 1L;
048
049 private URI normalizeURI(URI uri, Resource resourceContext) {
050 String fragment = uri.fragment();
051
052 if (fragment != null) {
053 int length = fragment.length();
054
055 if (length > 0 && fragment.charAt(0) != '/'
056 && fragment.charAt(length - 1) == '?') {
057
058 int index = fragment.lastIndexOf('?', length - 2);
059
060 if (index > 0) {
061 uri = uri.trimFragment().appendFragment(
062 fragment.substring(0, index));
063 }
064 }
065 }
066
067 if (uriConverter != null) {
068 return uriConverter.normalize(uri);
069 } else if (resourceContext != null) {
070 ResourceSet resourceSetContext = resourceContext
071 .getResourceSet();
072
073 if (resourceSetContext != null) {
074 return resourceSetContext.getURIConverter().normalize(uri);
075 }
076 }
077
078 return uri;
079 }
080
081 @Override
082 protected URI normalizeURI(URI uri, EObject objectContext) {
083 return normalizeURI(uri, objectContext.eResource());
084 }
085
086 @Override
087 protected void addProxy(EObject proxy, EObject context) {
088
089 if (proxy.eIsProxy()) {
090
091 if (proxyMap == null) {
092 proxyMap = createHashMap();
093 }
094
095 Resource resource = context.eResource();
096
097 if (resource != null) {
098 addAdapter(resource);
099 }
100
101 URI uri = normalizeURI(((InternalEObject) proxy).eProxyURI(),
102 resource);
103 List<EObject> proxies = proxyMap.get(uri);
104
105 if (proxies == null) {
106 proxyMap.put(uri,
107 proxies = new BasicEList.FastCompare<EObject>());
108 }
109
110 proxies.add(proxy);
111 }
112 }
113
114 protected Map<URI, List<EObject>> getProxyMap() {
115 return Collections.unmodifiableMap(proxyMap);
116 }
117 }
118
119 public static final CacheAdapter INSTANCE = createCacheAdapter();
120
121 private static CacheAdapter createCacheAdapter() {
122 CacheAdapter cacheAdapter = UML2Util
123 .loadClassFromSystemProperty("org.eclipse.uml2.common.util.CacheAdapter.INSTANCE"); //$NON-NLS-1$
124
125 if (cacheAdapter != null) {
126 return cacheAdapter;
127 }
128
129 return new CacheAdapter();
130 }
131
132 private final Map<Resource, Map<EObject, Map<Object, Object>>> values = Collections
133 .synchronizedMap(this
134 .<Resource, Map<EObject, Map<Object, Object>>> createHashMap());
135
136 protected boolean adapting = false;
137
138 private URIConverter uriConverter = null;
139
140 public static CacheAdapter getCacheAdapter(Notifier notifier) {
141 List<Adapter> eAdapters = notifier.eAdapters();
142
143 for (int i = 0, size = eAdapters.size(); i < size; i++) {
144 Object adapter = eAdapters.get(i);
145
146 if (adapter instanceof CacheAdapter) {
147 return (CacheAdapter) adapter;
148 }
149 }
150
151 return null;
152 }
153
154 public CacheAdapter() {
155 super();
156
157 unloadedEObjects = Collections.synchronizedMap(this
158 .<EObject, Resource> createHashMap());
159
160 unloadedResources = new Set<Resource>() {
161
162 Map<Resource, Object> map = new WeakHashMap<Resource, Object>();
163
164 public boolean add(Resource o) {
165
166 if (map.containsKey(o)) {
167 return false;
168 }
169
170 map.put(o, null);
171 return true;
172 }
173
174 public boolean addAll(Collection<? extends Resource> c) {
175 boolean result = false;
176
177 for (Resource resource : c) {
178
179 if (add(resource)) {
180 result = true;
181 }
182 }
183
184 return result;
185 }
186
187 public void clear() {
188 map.keySet().clear();
189 }
190
191 public boolean contains(Object o) {
192 return map.keySet().contains(o);
193 }
194
195 public boolean containsAll(Collection<?> c) {
196 return map.keySet().containsAll(c);
197 }
198
199 public boolean isEmpty() {
200 return map.keySet().isEmpty();
201 }
202
203 public Iterator<Resource> iterator() {
204 return map.keySet().iterator();
205 }
206
207 public boolean remove(Object o) {
208 return map.keySet().remove(o);
209 }
210
211 public boolean removeAll(Collection<?> c) {
212 return map.keySet().removeAll(c);
213 }
214
215 public boolean retainAll(Collection<?> c) {
216 return map.keySet().retainAll(c);
217 }
218
219 public int size() {
220 return map.keySet().size();
221 }
222
223 public Object[] toArray() {
224 return map.keySet().toArray();
225 }
226
227 public <T> T[] toArray(T[] a) {
228 return map.keySet().toArray(a);
229 }
230 };
231 }
232
233 protected <K, V> Map<K, V> createHashMap() {
234 return new HashMap<K, V>();
235 }
236
237 @Override
238 protected ECrossReferenceAdapter.InverseCrossReferencer createInverseCrossReferencer() {
239 return new InverseCrossReferencer();
240 }
241
242 protected boolean addAdapter(EList<Adapter> adapters) {
243 int index = adapters.indexOf(this);
244
245 switch (index) {
246 case 0 :
247 break;
248 case -1 :
249 adapters.add(0, this);
250 return true;
251 default :
252 adapters.move(0, index);
253 break;
254 }
255
256 return false;
257 }
258
259 public boolean adapt(Notifier notifier) {
260 boolean result = false;
261
262 if (notifier != null) {
263 adapting = true;
264 result = addAdapter(notifier.eAdapters());
265 adapting = false;
266 }
267
268 return result;
269 }
270
271 @Override
272 protected void addAdapter(Notifier notifier) {
273 addAdapter(notifier.eAdapters());
274 }
275
276 protected void addAdapter(EObject eObject) {
277
278 if (eObject != null && !eObject.eIsProxy()) {
279 Resource eResource = eObject.eResource();
280
281 if (eResource == null) {
282 addAdapter(EcoreUtil.getRootContainer(eObject).eAdapters());
283 } else {
284 ResourceSet resourceSet = eResource.getResourceSet();
285
286 if (resourceSet == null) {
287 addAdapter(eResource.eAdapters());
288 } else {
289 addAdapter(resourceSet.eAdapters());
290 }
291 }
292 }
293 }
294
295 @Override
296 public Collection<EStructuralFeature.Setting> getNonNavigableInverseReferences(
297 EObject eObject) {
298 addAdapter(eObject);
299
300 return super.getNonNavigableInverseReferences(eObject);
301 }
302
303 @Override
304 public Collection<EStructuralFeature.Setting> getInverseReferences(
305 EObject eObject) {
306 addAdapter(eObject);
307
308 return super.getInverseReferences(eObject);
309 }
310
311 public void handleCrossReference(EObject eObject) {
312 inverseCrossReferencer.add(eObject);
313 }
314
315 @Override
316 public void setTarget(Notifier target) {
317
318 if (!adapting) {
319 super.setTarget(target);
320 }
321 }
322
323 @Override
324 protected void unsetTarget(EObject target) {
325 super.unsetTarget(target);
326
327 // clear at resource scope iff not unloading
328 if (uriConverter == null) {
329 clear(target.eResource());
330 }
331 }
332
333 @Override
334 protected void unsetTarget(Resource target) {
335 super.unsetTarget(target);
336
337 clear(target);
338 }
339
340 @Override
341 public void notifyChanged(Notification msg) {
342 super.notifyChanged(msg);
343
344 Object notifier = msg.getNotifier();
345
346 if (notifier instanceof EObject) {
347 // clear at resource scope iff not touch
348 if (!msg.isTouch()) {
349 clear(((EObject) notifier).eResource());
350 }
351 } else if (notifier instanceof Resource) {
352
353 switch (msg.getFeatureID(Resource.class)) {
354 case Resource.RESOURCE__CONTENTS : {
355 clear();
356
357 if (uriConverter == null) {
358 Resource resource = (Resource) notifier;
359
360 if (!resource.isLoaded()) {
361 ResourceSet resourceSet = resource.getResourceSet();
362
363 if (resourceSet != null) {
364 // cache URI converter during unload
365 uriConverter = resourceSet.getURIConverter();
366 }
367 }
368 }
369
370 break;
371 }
372 case Resource.RESOURCE__IS_LOADED : {
373
374 if (!msg.getNewBooleanValue()) {
375 uriConverter = null;
376 }
377
378 break;
379 }
380 }
381 }
382 }
383
384 public void clear() {
385 values.clear();
386 }
387
388 public void clear(Resource resource) {
389 values.remove(resource);
390
391 if (resource != null) {
392 values.remove(null);
393 }
394 }
395
396 public boolean containsKey(EObject eObject, Object key) {
397 return containsKey(null, eObject, key);
398 }
399
400 public boolean containsKey(Resource resource, EObject eObject, Object key) {
401 Map<EObject, Map<Object, Object>> resourceMap = values.get(resource);
402
403 if (resourceMap != null) {
404 Map<Object, Object> eObjectMap = resourceMap.get(eObject);
405
406 if (eObjectMap != null) {
407 return eObjectMap.containsKey(key);
408 }
409 }
410
411 return false;
412 }
413
414 public Object get(EObject eObject, Object key) {
415 return get(null, eObject, key);
416 }
417
418 public Object get(Resource resource, EObject eObject, Object key) {
419 Map<EObject, Map<Object, Object>> resourceMap = values.get(resource);
420
421 if (resourceMap != null) {
422 Map<Object, Object> eObjectMap = resourceMap.get(eObject);
423
424 if (eObjectMap != null) {
425 return eObjectMap.get(key);
426 }
427 }
428
429 return null;
430 }
431
432 public Object put(EObject eObject, Object key, Object value) {
433 return put(null, eObject, key, value);
434 }
435
436 public Object put(Resource resource, EObject eObject, Object key,
437 Object value) {
438
439 if (key == null) {
440 throw new IllegalArgumentException(String.valueOf(key));
441 }
442
443 if (resource != null) {
444 addAdapter(resource);
445 }
446
447 Map<EObject, Map<Object, Object>> resourceMap = values.get(resource);
448
449 if (resourceMap == null) {
450 resourceMap = Collections.synchronizedMap(this
451 .<EObject, Map<Object, Object>> createHashMap());
452
453 values.put(resource, resourceMap);
454 }
455
456 Map<Object, Object> eObjectMap = resourceMap.get(eObject);
457
458 if (eObjectMap == null) {
459 eObjectMap = Collections.synchronizedMap(this
460 .<Object, Object> createHashMap());
461
462 resourceMap.put(eObject, eObjectMap);
463 }
464
465 return eObjectMap.put(key, value);
466 }
467
468 @Override
469 protected boolean resolve() {
470 return false;
471 }
472
473 @Override
474 protected boolean isIncluded(EReference eReference) {
475 return super.isIncluded(eReference) && eReference.isChangeable();
476 }
477
478 public Map<URI, List<EObject>> getProxyMap() {
479 return ((InverseCrossReferencer) inverseCrossReferencer).getProxyMap();
480 }
481
482 @Override
483 protected void resolveProxy(Resource resource, EObject eObject,
484 EObject proxy, EStructuralFeature.Setting setting) {
485 Resource eResource = setting.getEObject().eResource();
486
487 if (eResource != null
488 && eResource.getResourceSet() == resource.getResourceSet()) {
489
490 super.resolveProxy(resource, eObject, proxy, setting);
491 }
492 }
493
494 @Override
495 protected void selfAdapt(Notification notification) {
496 Object notifier = notification.getNotifier();
497
498 if (notifier instanceof Resource
499 && notification.getFeatureID(Resource.class) == Resource.RESOURCE__IS_LOADED) {
500
501 if (notification.getNewBooleanValue()) {
502 unloadedResources.remove(notifier);
503
504 for (Notifier child : ((Resource) notifier).getContents()) {
505 addAdapter(child);
506 }
507 } else {
508 unloadedResources.add((Resource) notifier);
509
510 synchronized (unloadedEObjects) {
511 for (Iterator<Map.Entry<EObject, Resource>> i = unloadedEObjects
512 .entrySet().iterator(); i.hasNext();) {
513
514 Map.Entry<EObject, Resource> entry = i.next();
515
516 if (entry.getValue() == notifier) {
517 i.remove();
518 EObject eObject = entry.getKey();
519 Collection<EStructuralFeature.Setting> settings = inverseCrossReferencer
520 .get(eObject);
521
522 if (settings != null) {
523
524 for (EStructuralFeature.Setting setting : settings) {
525 ((InverseCrossReferencer) inverseCrossReferencer)
526 .addProxy(eObject, setting.getEObject());
527 }
528 }
529 }
530 }
531 }
532 }
533 } else {
534 super.selfAdapt(notification);
535 }
536 }
537
538 }