| /* |
| * Copyright (c) 2011, 2012, 2014 Eike Stepper (Berlin, Germany) and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Victor Roldan Betancort - initial API and implementation |
| */ |
| package org.eclipse.emf.cdo.util; |
| |
| import org.eclipse.emf.cdo.CDOObject; |
| import org.eclipse.emf.cdo.CDOState; |
| import org.eclipse.emf.cdo.view.CDOObjectHandler; |
| import org.eclipse.emf.cdo.view.CDOView; |
| |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.EContentAdapter; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.emf.spi.cdo.FSMUtil; |
| import org.eclipse.emf.spi.cdo.InternalCDOView; |
| |
| import java.lang.ref.WeakReference; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * A scalable {@link EContentAdapter content adapter} that uses CDO mechanisms to attach itself to {@link CDOObject |
| * objects} when they are lazily loaded. |
| * |
| * @author Victor Roldan Betancort |
| * @since 4.0 |
| */ |
| public class CDOLazyContentAdapter extends EContentAdapter |
| { |
| private CDOObjectHandler handler = new CleanObjectHandler(); |
| |
| private Set<WeakReference<CDOObject>> adaptedObjects = new HashSet<WeakReference<CDOObject>>(); |
| |
| /** |
| * The root object to be adapted. |
| */ |
| private WeakReference<CDOObject> adaptedRoot; |
| |
| @Override |
| protected void setTarget(EObject target) |
| { |
| if (isConnectedObject(target)) |
| { |
| if (adaptedRoot == null) |
| { |
| adaptedRoot = new WeakReference<CDOObject>(CDOUtil.getCDOObject(target)); |
| } |
| |
| basicSetTarget(target); |
| if (target instanceof Resource) |
| { |
| addCleanObjectHandler(target); |
| } |
| } |
| else |
| { |
| super.setTarget(target); |
| } |
| } |
| |
| /** |
| * EContentAdapter removes adapter from all contained EObjects. In this case, we remove this adapter from all lazily |
| * loaded objects |
| */ |
| @Override |
| protected void unsetTarget(EObject target) |
| { |
| if (isConnectedObject(target)) |
| { |
| basicUnsetTarget(target); |
| if (target instanceof Resource) |
| { |
| InternalCDOView view = getCDOView(target); |
| if (view != null) |
| { |
| // Remove adapter from all adapted objects |
| for (WeakReference<CDOObject> weakReference : adaptedObjects) |
| { |
| CDOObject object = weakReference.get(); |
| if (object != null) |
| { |
| removeAdapter(object); |
| } |
| } |
| } |
| |
| target.eAdapters().remove(this); |
| removeCleanObjectHandler(target); |
| } |
| } |
| else |
| { |
| super.unsetTarget(target); |
| } |
| } |
| |
| private void addCleanObjectHandler(EObject target) |
| { |
| InternalCDOView view = getCDOView(target); |
| if (view != null) |
| { |
| CDOObjectHandler[] handlers = view.getObjectHandlers(); |
| for (CDOObjectHandler handler : handlers) |
| { |
| if (handler.equals(this.handler)) |
| { |
| return; |
| } |
| } |
| |
| view.addObjectHandler(handler); |
| |
| // Adapt already loaded objects |
| for (CDOObject cdoObject : view.getObjectsList()) |
| { |
| if (isContained(cdoObject)) |
| { |
| addAdapter(cdoObject); |
| } |
| } |
| } |
| } |
| |
| private void removeCleanObjectHandler(EObject target) |
| { |
| InternalCDOView view = getCDOView(target); |
| if (view != null) |
| { |
| CDOObjectHandler[] handlers = view.getObjectHandlers(); |
| for (CDOObjectHandler handler : handlers) |
| { |
| if (handler.equals(this.handler)) |
| { |
| view.removeObjectHandler(handler); |
| break; |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected void addAdapter(Notifier notifier) |
| { |
| if (isConnectedObject(notifier) && !isAlreadyAdapted(notifier)) |
| { |
| adaptedObjects.add(new WeakReference<CDOObject>(CDOUtil.getCDOObject((EObject)notifier))); |
| } |
| |
| super.addAdapter(notifier); |
| } |
| |
| private boolean isAlreadyAdapted(Notifier notifier) |
| { |
| return notifier.eAdapters().contains(this); |
| } |
| |
| private static InternalCDOView getCDOView(EObject target) |
| { |
| CDOObject object = CDOUtil.getCDOObject(target); |
| if (object != null) |
| { |
| return (InternalCDOView)object.cdoView(); |
| } |
| |
| return null; |
| } |
| |
| private static boolean isConnectedObject(Notifier target) |
| { |
| if (target instanceof EObject) |
| { |
| CDOObject object = CDOUtil.getCDOObject((EObject)target); |
| if (object != null) |
| { |
| return !FSMUtil.isTransient(object); |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Checks if the argument is contained in the object graph of the root element |
| */ |
| private boolean isContained(CDOObject object) |
| { |
| if (adaptedRoot == null) |
| { |
| return false; |
| } |
| |
| CDOObject root = adaptedRoot.get(); |
| if (object == null) |
| { |
| return false; |
| } |
| |
| if (root instanceof Resource) |
| { |
| return root == (object instanceof Resource ? object : object.cdoResource()); |
| } |
| |
| return EcoreUtil.isAncestor(root, object); |
| } |
| |
| /** |
| * @author Victor Roldan Betancort |
| */ |
| private final class CleanObjectHandler implements CDOObjectHandler |
| { |
| public void objectStateChanged(CDOView view, CDOObject object, CDOState oldState, CDOState newState) |
| { |
| if (newState == CDOState.CLEAN || newState == CDOState.NEW) |
| { |
| if (isConnectedObject(object) && !isAlreadyAdapted(object) && isContained(object)) |
| { |
| addAdapter(object); |
| } |
| } |
| } |
| } |
| } |