alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.java.codesync;

import java.util.*;

import org.openide.cookies.ConnectionCookie;
import org.openide.nodes.Node;
import org.openide.loaders.ConnectionSupport;
import org.openide.loaders.MultiDataObject;
import org.openide.src.*;

import org.netbeans.modules.java.bridge.LangModel;
import org.netbeans.modules.java.bridge.CommitListener;
import org.netbeans.modules.java.JavaConnections;

/**
 * The adapter transforms model's Commit events into legacy JavaConnections events.
 * The support is built from ConnectionSupport so it needs a connection to a DataObject.
 * The adapter may be refactored so that it optionally delegates to the ConnectionSupport
 * passed during construction, so direct dependency would not be mandatory.

* The support does not only direct transformation - for background compatibility and * usability of JavaConnections in general, the support blocks "immature" events, * which are such events that contain NOT_YET_RESOLVED identifiers by default. Changes, * creation of elements with such identifiers are collected in a cache and fired out only * after all element's properties are "mature".

* * @author sdedic * @version */ public class ModelEventAdapter extends ConnectionSupport implements CommitListener { public static final int FIELD = 0; public static final int METHOD = 1; public static final int CLASS = 2; public static final int CONSTRUCTOR = 3; public static final int INITIALIZER = 4; protected static final int TYPE_COUNT = 5; /** * Empty element array object. */ private static final Element[] EMPTY_ELEMENT_ARRAY = new Element[0]; /** * Empty change list array object. */ private static final JavaConnections.Change[] EMPTY_CHANGE_ARRAY = new JavaConnections.Change[0]; /** * Masks for the individual element types. */ private static final int[] EVENT_TYPES; static { EVENT_TYPES = new int[TYPE_COUNT]; EVENT_TYPES[FIELD] = JavaConnections.TYPE_FIELDS_ADD | JavaConnections.TYPE_FIELDS_REMOVE | JavaConnections.TYPE_FIELDS_CHANGE; EVENT_TYPES[METHOD] = JavaConnections.TYPE_METHODS_ADD | JavaConnections.TYPE_METHODS_REMOVE | JavaConnections.TYPE_METHODS_CHANGE; EVENT_TYPES[CLASS] = JavaConnections.TYPE_CLASSES_ADD | JavaConnections.TYPE_CLASSES_REMOVE | JavaConnections.TYPE_CLASSES_CHANGE; EVENT_TYPES[CONSTRUCTOR] = JavaConnections.TYPE_CONSTRUCTORS_ADD | JavaConnections.TYPE_CONSTRUCTORS_REMOVE | JavaConnections.TYPE_CONSTRUCTORS_CHANGE; EVENT_TYPES[INITIALIZER] = JavaConnections.TYPE_INITIALIZERS_ADD | JavaConnections.TYPE_INITIALIZERS_REMOVE | JavaConnections.TYPE_INITIALIZERS_CHANGE; } private static final String[] EVENT_TYPE_NAMES = { "Fields", "Methods", "Classes", "Constructors", "Initializers" // NOI18N }; private static final String[] EVENT_OPERATION_NAMES = { "0", "add", "remove", "undefined", "change", "5", "6", "7", // NOI18N }; /** * Model to watch. */ LangModel model; /** * Caches of individual change types. */ private ChangeCache[] cachedChanges; /** * Source of the outgoing events to fool ConnectionCookie.Event */ private Node fakeNodeSource; /** * Links the support to the source multidataobject.entry. */ private MultiDataObject.Entry sourceEntry; private static final boolean DEBUG = false; /** Creates new ModelEventAdapter. * @param entry [required] entry for the ConnectionSupport to function. Must not be null. */ public ModelEventAdapter(MultiDataObject.Entry entry) { super(entry, new ConnectionCookie.Type[] { new JavaConnections.Type(JavaConnections.TYPE_ALL)}); this.model = model; this.sourceEntry = entry; } /** * Returns true, if the adapter is attached. */ public boolean isAttached() { return model != null; } protected synchronized Node getSourceNode() { if (fakeNodeSource == null) fakeNodeSource = createSourceNode(); return fakeNodeSource; } protected Node createSourceNode() { return sourceEntry.getDataObject().getNodeDelegate(); } /** * Attaches the adapter to the model and starts listening on changes. * @param model model to listen on. * @throw IllegalStateException if the adapter is already attached to a model. */ public synchronized void attachToModel(LangModel model) { if (this.model != null) { if (model != this.model) throw new IllegalStateException("Already attached"); // NOI18N return; } model.addPostCommitListener(this); this.model = model; } /** * Detaches from the model. Clears the internal reference to the model. */ public synchronized void detachFromModel() { LangModel m = this.model; this.model = null; if (m == null) return; m.removePostCommitListener(this); } /** * Splits the `original' collection to several collection in the out array, * constructs them if necessary. It uses LinkedLists for the outgoing collections. * The method uses {@link #classifyElement} to obtain a typecode (index) of individual * collection items. * * @param original original collection of items * @param out factor collections derived from the original. */ private void splitCollection(Collection original, Collection[] out) { if (original == null || original.isEmpty()) return; for (Iterator it = original.iterator(); it.hasNext(); ) { Element el = (Element)it.next(); int kind = classifyElement(el); if (kind < 0) { if (DEBUG) System.err.println("Unknown kind: " + reportElementID(el)); // NOI18N continue; } if (out[kind] == null) out[kind] = new LinkedList(); if (DEBUG) System.err.println(reportElementID(el) + " -- kind = " + kind); // NOI18N out[kind].add(el); } } /** * Factors the `original' map to several `out' maps according to {@link #classifyElement} * factorization method. The original map is assumed to have elements in its keys. * @param original original map * @param out outgoing factored maps */ private void splitMap(Map original, Map[] out) { if (original == null || original.isEmpty()) return; for (Iterator it = original.entrySet().iterator(); it.hasNext(); ) { Map.Entry en = (Map.Entry)it.next(); Element el = (Element)en.getKey(); int kind = classifyElement(el); if (kind < 0) continue; if (out[kind] == null) out[kind] = new HashMap(19); if (DEBUG) { Element element = (org.openide.src.Element)((Object[])en.getValue())[0]; System.err.println("Change - newImpl = " + el.getCookie(Element.Impl.class) + " oldImpl = " + // NOI18N element.getCookie(Element.Impl.class)); } out[kind].put(el, en.getValue()); } } /** * Implementation of a {@link CommitListener}. This method first splits * elements according to their type, then fills them to appropriate caches. * If the {@link #getListenerMask mask} permits, it collects mature events * for the given type and fires all the events at once before exiting. */ public void changesCommited(Set created, Set removed, Map changed) { // first process the removed set: Element el; int elType; Collection creations[] = new Collection[TYPE_COUNT]; Collection removals[] = new Collection[TYPE_COUNT]; Map changes[] = new Map[TYPE_COUNT]; Collection allChanges = new LinkedList(); if (DEBUG) { if (created == null) System.err.println("No creations"); // NOI18N else System.err.println(created.size() + " creations"); // NOI18N if (removed == null) System.err.println("No removals"); // NOI18N else System.err.println(removed.size() + " removals"); // NOI18N } if (DEBUG) { System.err.println("Searching created"); // NOI18N } splitCollection(created, creations); if (DEBUG) { System.err.println("Searching removed"); // NOI18N } splitCollection(removed, removals); splitMap(changed, changes); int mask = getListenerMask(); for (int i = 0; i < EVENT_TYPES.length; i++) { Collection c = createChange(mask, i, creations[i], removals[i], changes[i]); if (c != null) allChanges.addAll(c); } if (allChanges.isEmpty()) return; if (DEBUG) reportFiredChanges(allChanges); int resultType = 0; // create composite type ORed from all the changes gathered for (Iterator it = allChanges.iterator(); it.hasNext(); ) { resultType |= ((JavaConnections.Change)it.next()).getChangeType(); } JavaConnections.Type wholeType = new JavaConnections.Type(resultType); JavaConnections.Change[] arr = (JavaConnections.Change[]) allChanges.toArray(new JavaConnections.Change[allChanges.size()]); // construct the event JavaConnections.Event evt = new JavaConnections.Event( getSourceNode(), arr, wholeType); // fire the event. fireEvent(evt); } private Collection createChange(int listenmask, int kind, Collection created, Collection removed, Map changed) { if (created == null && removed == null && changed == null) { return null; } ChangeCache cache = getEventCache(kind); Collection changePairs; if (created != null) created = cache.elementsCreated(created); if (removed != null) removed = cache.elementsRemoved(removed); if (changed != null) changePairs = cache.elementsChanged(changed); else changePairs = null; int baseType = EVENT_TYPES[kind]; int mask = listenmask & baseType; // mask = JavaConnections.CHANGE_MASK | JavaConnections.ADD_MASK | JavaConnections.REMOVE_MASK; if (DEBUG) { System.err.println("Creating events for mask " + baseType + // NOI18N "known listener(s) for mask " + listenmask); // NOI18N } if (created == null && removed == null && changePairs == null) { if (DEBUG) System.err.println("*no change"); // NOI18N return null; } // is there a listener registered for this type of event(s) ? if (mask == 0 && !DEBUG) return null; Collection out = new LinkedList(); Change ch; if (changePairs != null) { // have to iterate anyway to find out creates hidden as changes boolean accept = DEBUG || (mask & JavaConnections.CHANGE_MASK) > 0; for (Iterator it = changePairs.iterator(); it.hasNext(); ) { Element eNew = (Element)it.next(); Element eOld = (Element)it.next(); if (eOld == null) { if (created == null) created = new LinkedList(); created.add(eNew); continue; } if (!accept) continue; ch = new Change(baseType & JavaConnections.CHANGE_MASK, eOld, eNew); if (DEBUG) reportChange(ch); if (!DEBUG || (mask & JavaConnections.CHANGE_MASK) > 0) { out.add(ch); } } } if (created != null) { if (DEBUG || (mask & JavaConnections.ADD_MASK) > 0) { ch = new Change( baseType & JavaConnections.ADD_MASK, created); if (DEBUG) reportChange(ch); if (!DEBUG || (mask & JavaConnections.ADD_MASK) > 0) out.add(ch); } } if (removed != null) { if (DEBUG || (mask & JavaConnections.REMOVE_MASK) > 0) { ch = new Change(baseType & JavaConnections.REMOVE_MASK, removed); if (DEBUG) reportChange(ch); if (!DEBUG || (mask & JavaConnections.REMOVE_MASK) > 0) { out.add(ch); } } } return out; } private static class Change extends JavaConnections.Change { Change(int type, Collection items) { super(type, null, null, (Element[])items.toArray(EMPTY_ELEMENT_ARRAY)); } Change(int type, Element old, Element n) { super(type, old, n, null); } } ChangeCache[] eventCaches = new ChangeCache[TYPE_COUNT]; protected ChangeCache getEventCache(int kind) { if (eventCaches[kind] != null) return eventCaches[kind]; return eventCaches[kind] = new Cache(kind); } /** * Creates a type tag for the element. */ protected int classifyElement(Element el) { if (el instanceof ClassElement) { return CLASS; } else if (el instanceof ConstructorElement) { if (el instanceof MethodElement) return METHOD; else return CONSTRUCTOR; } else if (el instanceof FieldElement) { return FIELD; } else if (el instanceof InitializerElement) { return INITIALIZER; } return -1; } protected class Cache extends ChangeCache { int elementType; Cache(int type) { elementType = type; } protected boolean isMature(Element one, Element two) { return ModelEventAdapter.this.isMature(elementType, one, two); } } /** * Returns a mask for element types to look for. This mask is derived from * JavaConnection type mask. */ protected int getListenerMask() { ConnectionCookie.Type t; Collection regTypes = getRegisteredTypes(); int mask = 0; for (Iterator it = regTypes.iterator(); it.hasNext(); ) { t = (ConnectionCookie.Type)it.next(); if (t instanceof JavaConnections.Type) { mask |= ((JavaConnections.Type)t).getFilter(); } else { System.err.println("WARNING: Unknown listener type: " + t); // NOI18N } } return mask; } protected boolean checkType(org.openide.src.Type t) { if (t.isPrimitive()) return true; while (t.isArray()) t = t.getElementType(); if (t.isPrimitive()) return true; return checkIdentifier(t.getTypeIdentifier()); } protected boolean checkIdentifier(Identifier id) { if (id == null) return true; return id.getResolutionStatus() != Identifier.NOT_YET_RESOLVED; } protected boolean isMature(int type, Element test, Element hint) { if (!DEBUG) return checkIsMature(type, test, hint); boolean res = checkIsMature(type, test, hint); if (!res) System.err.println("-- immature"); // NOI18N return res; } private void reportChange(JavaConnections.Change ch) { int type = ch.getChangeType(); int op = type; StringBuffer sb = new StringBuffer(); for (int i = 0; i < TYPE_COUNT; i++) { if ((type & EVENT_TYPES[i]) == 0) { op = op >> 4; continue; } sb.append(EVENT_TYPE_NAMES[i]); sb.append('-'); sb.append(EVENT_OPERATION_NAMES[op & 0x07]); sb.append('('); sb.append(new Integer(op).toString()); sb.append(')'); } sb.append(':'); if ((type & JavaConnections.ADD_MASK) > 0) { reportElementIDs(sb, Arrays.asList(ch.getElements())); } if ((type & JavaConnections.REMOVE_MASK) > 0) { reportElementIDs(sb, Arrays.asList(ch.getElements())); } if ((type & JavaConnections.CHANGE_MASK) > 0) { reportElementIDs(sb, Collections.singleton(ch.getNewElement())); } System.err.println(sb.toString()); } private void reportFiredChanges(Collection changes) { for (Iterator it = changes.iterator(); it.hasNext(); ) { JavaConnections.Change ch = (JavaConnections.Change)it.next(); reportChange(ch); } } static String reportElementID(Element e) { StringBuffer sb = new StringBuffer(); reportElementIDs(sb, Collections.singletonList(e)); return sb.toString(); } static void reportElementIDs(StringBuffer sb, Collection c) { boolean first = true; for (Iterator it = c.iterator(); it.hasNext(); ) { if (!first) sb.append(", "); // NOI18N Element el = (Element)it.next(); if (el instanceof FieldElement) { sb.append(((FieldElement)el).getName().toString()); } else if (el instanceof InitializerElement) { sb.append(""); // NOI18N } else if (el instanceof ConstructorElement) { sb.append(((ConstructorElement)el).getName().toString()); sb.append(reportParameters((ConstructorElement)el)); } else if (el instanceof ClassElement) { sb.append(((ClassElement)el).getName().getFullName()); } else { sb.append("<" + el + ">"); // NOI18N continue; } first = false; } } private static String reportParameters(ConstructorElement e) { StringBuffer sb = new StringBuffer(); sb.append('('); MethodParameter[] pars = e.getParameters(); for (int i = 0; i < pars.length; i++) { if (i > 0) sb.append(", "); // NOI18N sb.append(pars[i].toString()); } sb.append(')'); return sb.toString(); } private boolean checkIsMature(int type, Element test, Element hint) { switch (type) { case FIELD: if (DEBUG) System.err.println("Field " + ((FieldElement)test).getName()); // NOI18N return checkType(((FieldElement)test).getType()); case METHOD: if (DEBUG) { MethodElement m = (MethodElement)test; StringBuffer sb = new StringBuffer(m.getName().getName()); sb.append('('); MethodParameter[] pars = m.getParameters(); for (int i = 0; i < pars.length; i++) { sb.append(pars[i].toString()); } sb.append(')'); System.err.println("Method " + sb.toString()); // NOI18N } if (!checkType(((MethodElement)test).getReturn())) return false; /* FALL THROUGH ! */ case CONSTRUCTOR: { ConstructorElement cons = (ConstructorElement)test; if (DEBUG && !(cons instanceof MethodElement)) { StringBuffer sb = new StringBuffer(cons.getName().getName()); sb.append('('); MethodParameter[] pars = cons.getParameters(); for (int i = 0; i < pars.length; i++) { sb.append(pars[i].toString()); } sb.append(')'); System.err.println("Constructor " + sb.toString()); // NOI18N } MethodParameter[] pars = cons.getParameters(); for (int i = 0; i < pars.length; i++) { if (!checkType(pars[i].getType())) return false; } Identifier exs[] = cons.getExceptions(); for (int i = 0; i < exs.length; i++) { if (!checkIdentifier(exs[i])) return false; } break; } case CLASS: { ClassElement c = (ClassElement)test; if (DEBUG) System.err.println("Class " + c.getName().getFullName()); // NOI18N if (!checkIdentifier(c.getSuperclass())) return false; Identifier[] its = c.getInterfaces(); for (int i = 0; i < its.length; i++) if (!checkIdentifier(its[i])) return false; break; } } return true; } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.