/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.core.util; import java.util.List; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.teiid.core.designer.ModelerCoreException; import org.teiid.core.designer.util.CoreArgCheck; /** * The ConcurrentModelVisitorProcessor is used to walk a {@link ModelVisitor} implementation through one or more * models. This class extends ModelVisitorProcessor overloading implementations of the walk method in order * to add logic to prevent a ConcurrentModificationException while processing a list of children. The logic * to prevent a ConcurrentModificationException is to create an array copy of the child list before iterating * over the children. As a result of the array copy operations ConcurrentModelVisitorProcessor is less * efficient that using ModelVisitorProcessor. * <p> * To use, simply create a {@link ModelVisitor visitor} and a ModelProcessor, and use the processor * to walk one or more objects by supply the starting point(s): * <pre> * final ModelVisitor visitor = new MyVisitor(); * final ModelVisitorProcessor processor = new ModelVisitorProcessor(visitor); * try { * processor.walk(startingPoint,ModelVisitorProcessor.DEPTH_INFINITE); * } catch (ModelerCoreException e) { * // handle the exception * } * </pre> * where <code>startingPoint</code> can be one of the following: * <ul> * <li>An {@link EObject}</li> * <li>An {@link Resource EMF Resource}</li> * <li>A {@link org.teiid.designer.core.workspace.ModelResource ModelResource}</li> * <li>A {@link org.teiid.designer.core.workspace.ModelFolder ModelFolder}</li> * <li>A {@link org.teiid.designer.core.workspace.ModelProject ModelProject}</li> * <li>A {@link org.teiid.designer.core.workspace.ModelWorkspace ModelWorkspace}</li> * <li>A {@link List list} of any of the above objects</li> * </ul> * and where <code>depth</code> is one of the following: * <ul> * <li>{@link org.teiid.designer.core.util.ConcurrentModelVisitorProcessor#DEPTH_INFINITE ModelVisitorProcessor.DEPTH_INFINITE}</li> * <li>{@link org.teiid.designer.core.util.ConcurrentModelVisitorProcessor#DEPTH_ONE ModelVisitorProcessor.DEPTH_ONE}</li> * <li>{@link org.teiid.designer.core.util.ConcurrentModelVisitorProcessor#DEPTH_ZERO ModelVisitorProcessor.DEPTH_ZERO}</li> * </ul> * </p> * @see ModelVisitor. * * @since 8.0 */ public class ConcurrentModelVisitorProcessor extends ModelVisitorProcessor { /** * Construct an instance of ModelWalker. * */ public ConcurrentModelVisitorProcessor( final ModelVisitor visitor ) { this(visitor,MODE_DEFAULT); } /** * Construct an instance of ModelWalker. * */ public ConcurrentModelVisitorProcessor( final ModelVisitor visitor, final int mode ) { super(visitor,mode); } /** * Walk the supplied {@link Resource} to the supplied depth. * The {@link #getModelVisitor() model visitor} is only called when an {@link EObject} or * {@link Resource EMF Resource} is found. Of course, because the {@link #getModelVisitor() model visitor} * can decide whether or not children are to be visited, the supplied depth is really considered * to be the maximum depth allowed. * @param startingResource the EMF resource to be walked; may not be null * @param depth the depth to walk; must be one of {@link #DEPTH_INFINITE DEPTH_INFINITE}, * {@link #DEPTH_ONE DEPTH_ONE} or {@link #DEPTH_ZERO DEPTH_ZERO} * @throws ModelerCoreException if there is an exception walking the resource */ @Override public void walk( final Resource startingResource, final int depth ) throws ModelerCoreException { CoreArgCheck.isNotNull(startingResource); assertValidDepth(depth); // visit this resource if (!getModelVisitor().visit(startingResource) || depth == DEPTH_ZERO) return; // visit the children - use an array to avoid ConcurrentModificationException on contents list final int nextDepth = (depth == DEPTH_INFINITE ? DEPTH_INFINITE : DEPTH_ZERO ); final Object[] children = startingResource.getContents().toArray(); for (int i = 0; i != children.length; ++i) { final Object obj = children[i]; if ( obj instanceof EObject ) { walk( (EObject)obj, nextDepth ); } } } /** * Walk to the supplied depth the tree of model objects below the supplied {@link EObject}. * The {@link #getModelVisitor() model visitor} is only called when an {@link EObject} or * {@link Resource EMF Resource} is found. Of course, because the {@link #getModelVisitor() model visitor} * can decide whether or not children are to be visited, the supplied depth is really considered * to be the maximum depth allowed. * @param startingResource the root of the tree of model objects to be walked; may not be null * @param depth the depth to walk; must be one of {@link #DEPTH_INFINITE DEPTH_INFINITE}, * {@link #DEPTH_ONE DEPTH_ONE} or {@link #DEPTH_ZERO DEPTH_ZERO} * @throws ModelerCoreException if there is an exception walking the model objects */ @Override public void walk( final EObject startingObject, final int depth ) throws ModelerCoreException { CoreArgCheck.isNotNull(startingObject); assertValidDepth(depth); // visit this resource if (!this.visitor.visit(startingObject) || depth == DEPTH_ZERO) return; // final URI uri = EcoreUtil.getURI(startingObject); // System.out.println("Visiting " + uri.fragment() + " (" + startingObject.eClass().getName() + ")"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ // visit the children final int nextDepth = (depth == DEPTH_INFINITE ? DEPTH_INFINITE : DEPTH_ZERO ); final Object children = this.navigator.getChildren(startingObject); if ( children instanceof List ) { final Object[] tmp = ((List)children).toArray(); for (int i = 0; i != tmp.length; ++i) { final Object obj = tmp[i]; if ( obj instanceof EObject ) { walk( (EObject)obj, nextDepth ); } } } else if ( children instanceof EObject ) { walk((EObject)children,nextDepth); } if ( this.visitor instanceof ModelVisitorWithFinish ) { ((ModelVisitorWithFinish)this.visitor).postVisit(startingObject); } } }