/******************************************************************************* * Copyright (c) 2001, 2010 IBM Corporation 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: * IBM Corporation - initial API and implementation * Jens Lukowski/Innoopract - initial renaming/restructuring * *******************************************************************************/ package org.eclipse.wst.sse.core.internal.provisional; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import org.eclipse.core.runtime.Platform; import org.eclipse.wst.sse.core.internal.Logger; import org.eclipse.wst.sse.core.internal.model.FactoryRegistry; /** * AbstractNotifier is similar to (and based on) the EMF NotifierImpl class, * but is not related to EMF per se. This class is simpler (that is, not as * many functions). * * Implementers of this INodeNotifier must subclass this class. */ public abstract class AbstractNotifier implements INodeNotifier { private final static int growthConstant = 3; private int adapterCount = 0; private INodeAdapter[] fAdapters; /** * AbstractNotifier constructor comment. */ public AbstractNotifier() { super(); } /** * addAdapter method comment. */ public synchronized void addAdapter(INodeAdapter adapter) { if (adapter == null) return; ensureCapacity(adapterCount + 1); fAdapters[adapterCount++] = adapter; } private synchronized void ensureCapacity(int needed) { if (fAdapters == null) { // first time fAdapters = new INodeAdapter[needed + growthConstant]; return; } int oldLength = fAdapters.length; if (oldLength < needed) { INodeAdapter[] oldAdapters = fAdapters; INodeAdapter[] newAdapters = new INodeAdapter[needed + growthConstant]; System.arraycopy(oldAdapters, 0, newAdapters, 0, adapterCount); fAdapters = newAdapters; } } /** * NOT API: used only for testing. * * @return int */ public int getAdapterCount() { return adapterCount; } /** * Default behavior for getting an adapter. */ public synchronized INodeAdapter getAdapterFor(Object type) { // first, we'll see if we already have one INodeAdapter result = getExistingAdapter(type); // if we didn't find one in our list already, // let's create it if (result == null) { FactoryRegistry reg = getFactoryRegistry(); if (reg != null) { INodeAdapterFactory factory = reg.getFactoryFor(type); if (factory != null) { result = factory.adapt(this); } } // We won't prevent null from being returned, but it would be // unusual. // It might be because Factory is not working correctly, or // not installed, so we'll allow warning message. if ((result == null) && (org.eclipse.wst.sse.core.internal.util.Debug.displayWarnings)) { System.out.println("Warning: no adapter was found or created for " + type); //$NON-NLS-1$ } } return result; } /** * Returns a shallow clone of list, since clients should not manipulate * our list directly. Instead, they should use add/removeAdapter. */ public synchronized Collection getAdapters() { if (fAdapters != null) { if (adapterCount == 0) { fAdapters = null; return Collections.EMPTY_LIST; } else { // we need to make a new array, to be sure // it doesn't contain nulls at end, which may be // present there for "growth". INodeAdapter[] tempAdapters = new INodeAdapter[adapterCount]; System.arraycopy(fAdapters, 0, tempAdapters, 0, adapterCount); // EMF uses the unmodifiableCollection. Its a bit of a // performance // drain, but may want to leave in since // it would "fail fast" if someone was trying to modify the // list. return Collections.unmodifiableCollection(Arrays.asList(tempAdapters)); // return Arrays.asList(newAdapters); } } else return Collections.EMPTY_LIST; } private long getAdapterTimeCriteria() { // to "re-get" the property each time is a little awkward, but we // do it that way to avoid adding instance variable just for // debugging. // This method should only be called if debugAdapterNotifcationTime // is true. final String criteriaStr = Platform.getDebugOption("org.eclipse.wst.sse.core/dom/adapter/notification/time/criteria"); //$NON-NLS-1$ long criteria = -1; if (criteriaStr != null) { try { criteria = Long.parseLong(criteriaStr); } catch (NumberFormatException e) { // catch to be sure we don't burb in notification loop, // but ignore, since just a debug aid } } return criteria; } public synchronized INodeAdapter getExistingAdapter(Object type) { INodeAdapter result = null; for (int i = 0; i < adapterCount; i++) { INodeAdapter a = fAdapters[i]; if (a != null && a.isAdapterForType(type)) { result = a; break; } } // if we didn't find one in our list, // return the null result return result; } abstract public FactoryRegistry getFactoryRegistry(); public void notify(int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { int localAdapterCount = 0; INodeAdapter[] localAdapters = null; // lock object while making local assignments synchronized (this) { if (fAdapters != null) { localAdapterCount = adapterCount; localAdapters = new INodeAdapter[localAdapterCount]; System.arraycopy(fAdapters, 0, localAdapters, 0, localAdapterCount); } } for (int i = 0; i < localAdapterCount; i++) { INodeAdapter a = localAdapters[i]; if (Logger.DEBUG_ADAPTERNOTIFICATIONTIME) { long getAdapterTimeCriteria = getAdapterTimeCriteria(); long startTime = System.currentTimeMillis(); // ** keep this line identical with non-debug version!! a.notifyChanged(this, eventType, changedFeature, oldValue, newValue, pos); long notifyDuration = System.currentTimeMillis() - startTime; if (getAdapterTimeCriteria >= 0 && notifyDuration > getAdapterTimeCriteria) { System.out.println("adapter notifyDuration: " + notifyDuration + " class: " + a.getClass()); //$NON-NLS-1$ //$NON-NLS-2$ } } else { try { // ** keep this line identical with debug version!! a.notifyChanged(this, eventType, changedFeature, oldValue, newValue, pos); } catch (Exception e) { // Its important to "keep going", since notifications // occur between an // aboutToChange event and a changed event -- the // changed event typically being require // to restore state, etc. So, we just log message, do // not re-throw it, but // typically the exception does indicate a serious // program error. Logger.logException("A structured model client, " + a + " threw following exception during adapter notification (" + INodeNotifier.EVENT_TYPE_STRINGS[eventType] + " )", e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } } } public synchronized void removeAdapter(INodeAdapter a) { if (fAdapters == null || a == null) return; int newIndex = 0; INodeAdapter[] newAdapters = new INodeAdapter[fAdapters.length]; int oldAdapterCount = adapterCount; boolean found = false; for (int oldIndex = 0; oldIndex < oldAdapterCount; oldIndex++) { INodeAdapter candidate = fAdapters[oldIndex]; if (a == candidate) { adapterCount--; found = true; } else newAdapters[newIndex++] = fAdapters[oldIndex]; } if (found) fAdapters = newAdapters; } }