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    }