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.bridge;

import java.beans.PropertyChangeEvent;
import java.util.*;
import org.openide.src.*;
import org.netbeans.modules.java.ElementFactory.Item;
import org.netbeans.modules.java.JavaDataObject;
import org.netbeans.jmi.javamodel.JavaClass;
import org.netbeans.jmi.javamodel.Constructor;
import org.netbeans.jmi.javamodel.Method;
import org.netbeans.jmi.javamodel.Initializer;
import org.netbeans.jmi.javamodel.Field;
import org.netbeans.jmi.javamodel.Import;
import org.netbeans.api.mdr.MDRepository;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.netbeans.modules.javacore.JMManager;
import org.openide.ErrorManager;

/**
 * This is the root point of the source text model. This object creates individual
 * model's elements and performs model-related tasks. All elements in the model have
 * (indirect) references to this object so they can contact it.
 * @author  svata
 * @version 
 */
public class DefaultLangModel implements LangModel, LangModel.Updater, Runnable {
    private static boolean  initialized;
    
    private static Class    CLASS_MEMBER_IMPL;
    
    private static Class    CLASS_INITIALIZER_IMPL;
    
    static final Positioner DEFAULT_POSITIONER = new DefaultInsertStrategy();
    
    /** Mutex object for the whole model. The lock is obtained during model updates so
     * change detection and property changes are really atomic.
     */
    private Object      locked;
    
    private Env         env;
    
    private Object      writerNotify;
    
    private Thread      writingThread;
    
    private int         readerCount;
    
    private Thread      reader;
    
    // derived attributes:
    private BindingFactory  bindingFactory;
    
    private WrapperFactory  wrapperFactory;
    
    /** Event queue map for individual elements in the model. The map is keyed by
     * element instances, values maps  -> 
     */
    private EventQueue  eventQueue;
    
    /** Number of nested atomic locks.
     */
    private int         writeLocks;
    
    private int         masterLocks;
    
    /**
     * True, if all changes made in the transaction should be commited upon lock
     * release.
     */
    private boolean     transactionCommited;
    
    /**
     * True, if the transaction is constrained - changes are consulted with
     * VetoableChangeListeners and checked for consistency.
     */
    private boolean     transactionConstrained;
    
    /**
     * True, if the model is currently dispatching events.
     */
    private boolean     eventDispatch;

    /**
     * List of currently registered pre-commit listners.
     */
    Collection          preCommitListeners;
    
    /**
     * List of currently registered post-commit listeners.
     */
    Collection          postCommitListeners;
    
    /**
     * Holds top-level EventQueues in the order of dispatching. New queues are appened
     * during unlock to preserve the order of event generation.
     */
    LinkedList           outputQueue;
    
    boolean              firingEvents;
    
    private JavaDataObject javaDataObject;
    
    public DefaultLangModel(JavaDataObject jdo) {
        this (new LangEnvImpl (jdo), jdo);
    }
     
    public DefaultLangModel(Env env, JavaDataObject jdo) {
        this.env = env;
        this.javaDataObject = jdo;
        writerNotify = new Object();
        this.eventQueue = new EventQueue(null);
    }
    
    public JavaDataObject getJavaDataObject () {
        return javaDataObject;
    }
    
    private static void initializeClasses() {
        if (initialized)
            return;
        try {
            CLASS_MEMBER_IMPL = Class.forName("org.netbeans.modules.java.bridge.MemberElementImpl"); // NOI18N
            CLASS_INITIALIZER_IMPL = Class.forName("org.netbeans.modules.java.bridge.InitializerElementImpl"); // NOI18N
        } catch (ClassNotFoundException ex) {
            // warn -- the class was not found.
        }
        initialized = true;
    }
    
    /** Clone the model object; the method does not clone the whole model, it rather clones
     * the management object so that objects created by the clone are compatible with
     * the original one. Usable mainly for paralel analysis of two sources and matching
     * them up.
     */
    public Object clone() {
        return null;
    }
    
    /*----------------------------------------------------------------  
     * Factory methods for creating implementations of 
     * model pieces
     *----------------------------------------------------------------*/    
    
    public ClassElementImpl createTopClass(SourceElement src, JavaClass javaClass) {
	ClassElementImpl c = new ClassElementImpl(this, javaClass);
        getWrapper().wrapClass(c, src);
        c.setParent(src);
        return c;
    }
    
    public ClassElementImpl createInnerClass(ClassElement parent, JavaClass javaClass) {
	ClassElementImpl c = new ClassElementImpl(this, javaClass);
        getWrapper().wrapClass(c, parent);
        c.setParent(parent);
        return c;
    }
    
    public FieldElementImpl createField(ClassElement parent, Field field) {
        FieldElementImpl impl = new FieldElementImpl(this, field);                        
        getWrapper().wrapField(impl, parent);
        impl.setParent(parent);        
        return impl;
    }
    
    public ConstructorElementImpl createConstructor(ClassElement parent, Constructor constructor) {
        ConstructorElementImpl impl = new ConstructorElementImpl(this, constructor);
        getWrapper().wrapConstructor(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    public MethodElementImpl createMethod(ClassElement parent, Method method) {
        MethodElementImpl impl = new MethodElementImpl(this, method);
        getWrapper().wrapMethod(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    public ImportImpl createImport(SourceElement parent, Import imp) {
        ImportImpl impl = new ImportImpl(this, imp);
        getWrapper().wrapImport(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    public InitializerElementImpl createInitializer(ClassElement parent, Initializer initializer) {
        InitializerElementImpl impl = new InitializerElementImpl(this, initializer);
        getWrapper().wrapInitializer(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    // ..........................................................................
    
    public SourceElementImpl createSource() {
        MDRepository repo = JavaMetamodel.getDefaultRepository();
        repo.beginTrans(false);
        try {
            return new SourceElementImpl(this, null, null);
        } finally {
            repo.endTrans();
        }
    }
    
    public ClassElementImpl createTopClass(SourceElement src) {
	ClassElementImpl c = new ClassElementImpl(this, null);
        getWrapper().wrapClass(c, src);
        c.setParent(src);
        return c;
    }
    
    public ClassElementImpl createInnerClass(ClassElement parent) {
	ClassElementImpl c = new ClassElementImpl(this, null);
        getWrapper().wrapClass(c, parent);
        c.setParent(parent);
        return c;
    }
    
    public FieldElementImpl createField(ClassElement parent) {
        FieldElementImpl impl = new FieldElementImpl(this, null);
        getWrapper().wrapField(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    public ConstructorElementImpl createConstructor(ClassElement parent) {
        ConstructorElementImpl impl = new ConstructorElementImpl(this, null);
        getWrapper().wrapConstructor(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    public MethodElementImpl createMethod(ClassElement parent) {
        MethodElementImpl impl = new MethodElementImpl(this, null);
        getWrapper().wrapMethod(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    public ImportImpl createImport(SourceElement parent) {
        ImportImpl impl = new ImportImpl(this, null);
        getWrapper().wrapImport(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    public InitializerElementImpl createInitializer(ClassElement parent) {
        InitializerElementImpl impl = new InitializerElementImpl(this, null);
        getWrapper().wrapInitializer(impl, parent);
        impl.setParent(parent);
        return impl;
    }
    
    // ..........................................................................
    
    final Identifier resolveIdent(Element context, Identifier original) {
        return env.resolveTypeIdent(context, original);
    }
    
    final Type resolveType(Element context, Type t) {
        return env.resolveType(context, t);
    }
    
    public void addPreCommitListener(CommitListener l) {
        synchronized (this) {
            if (preCommitListeners == null)
                preCommitListeners = new LinkedList();
        }
        synchronized (preCommitListeners) {
            preCommitListeners.add(l);
        }
    }
    
    public void addPostCommitListener(CommitListener l) {
        synchronized (this) {
            if (postCommitListeners == null)
                postCommitListeners = new LinkedList();
        }
        synchronized (postCommitListeners) {
            postCommitListeners.add(l);
        }
    }
    
    public void removePreCommitListener(CommitListener l) {
        if (preCommitListeners == null)
            return;
        synchronized (preCommitListeners) {
            preCommitListeners.remove(l);
        }
    }
    
    public void removePostCommitListener(CommitListener l) {
        if (postCommitListeners == null)
            return;
        synchronized (postCommitListeners) {
            postCommitListeners.remove(l);
        }
    }
    
    final void notifyEventsDispatched(boolean dispatchOn) {
        this.eventDispatch = dispatchOn;
    }
    
    public final Object writeLock() {
        return null;
        /*
        Object l = doWriteLock();
        if (masterLocks == -1) 
            createEventQueue();
        return l;
         */
    }
    
    private void createEventQueue() {
        eventQueue = new EventQueue(eventQueue);
    }
    
    /**
     * The method obtains a write lock, but integrates all messages to the outside
     * queue instead of creating a local one.
     */
    final Object masterWriteLock() {
        /*
        Object l = doWriteLock();
        if (masterLocks > 0) {
            try {
                releaseWriteLock(l);
            } catch (SourceException ex) {
                // should NOT happen.
            }
            throw new IllegalStateException("Nested master locks!!"); // NOI18N
        }
        masterLocks = writeLocks;
        eventQueue = new EventQueue(eventQueue);
        return l;
         */
        return null;
    }
    
    public Object tryWriteLock() {
        return tryWriteLock(false);
    }
    
    private Object tryWriteLock(boolean master) {
        /*
        synchronized (writerNotify) {
            if (locked == null ||
                writingThread == Thread.currentThread())
                return master ?
                    masterWriteLock() :
                    writeLock();
        }
        return null;
         */
        return null;
    }
    
    public final Object doWriteLock() {
        /*
        synchronized (writerNotify) {
            if (writingThread == Thread.currentThread()) {
                if (eventDispatch) {
                    throw new IllegalStateException(
                        "Modification from inside the event handler are banned"); // NOI18N
                }
                writeLocks++;
                return locked;
            }
            if (locked != null) {
                try {
                    writerNotify.wait();
                } catch (InterruptedException ex) {
                    throw new IllegalStateException("Interrupted"); // NOI18N
                }
            }
            // new lock - constrain the transaction.
            transactionConstrained = true;
            writeLocks++;
            locked = writerNotify;
            this.writingThread = Thread.currentThread();
            return locked;
        }
         */
        return null;
    }
    
    /**
     * Releases the write lock held on the model. If there are any outstanding
     * events, the CommitListener events are fired prior to releasing the lock.
     * If the operation is not aborted, write lock is released and queued 
     * PropertyChangeEvents are fired out. 
     */
    public final void releaseWriteLock(Object handle) {
        releaseWriteLock(handle, false);
    }
    
    final void releaseWriteLock(Object handle, boolean forkThread) {
        /*
        if (handle == null)
            throw new IllegalArgumentException("Invalid lock: " + handle); // NOI18N
        if (locked == null)
            throw new IllegalStateException("Model not locked."); // NOI18N
        synchronized (writerNotify) {
            if (handle != locked)
                throw new IllegalArgumentException("Invalid unlock attempt."); // NOI18N
        }
        
        EventQueue result = null;
        
        if (masterLocks == -1)
            result = mergeEventQueues();
        else if (masterLocks >= writeLocks) {
            result = mergeEventQueues();
            masterLocks = -1;
        }
        if (--writeLocks > 0) {
            return;
        }
        eventQueue = null;

        // fix change events so they contain snapshots of
        // new state in addition to the live reference.        
        
        // PENDING
        if (result != null) {
            result.fixupChanges();
            // can throw SourceException
            firePreCommitEvents(result);
            enqueue(result);
        }
        
        synchronized (writerNotify) {
            this.writingThread = null;
            locked = null;
            writerNotify.notifyAll();
        }
        if (forkThread) {
            org.openide.util.RequestProcessor.getDefault().post(this);
        } else {
            processOutputQueue();
        }
         */
    }
    
    /**
     * This method operates under a write lock -- nobody else can modify or create the queue,
     * but somebody may be reading it.
     */
    private void enqueue(EventQueue q) {
        /*
        if (outputQueue == null)
            outputQueue = new LinkedList();
        synchronized (outputQueue) {
            outputQueue.addLast(q);
        }
         */
    }
    
    public void run() {
        // processOutputQueue();
    }
    
    private void processOutputQueue() {
        /*
        if (this.outputQueue == null)
            return;
        
        EventQueue bit;
        synchronized (outputQueue) {
            if (firingEvents)
                return;
            firingEvents = true;
        }
        try {
            while (true) {
                synchronized (outputQueue) {
                    if (outputQueue.isEmpty()) {
                        return;
                    }
                    bit = (EventQueue)outputQueue.removeFirst();
                }
                bit.fireEvents();
                firePostCommitEvents(bit);
            }
        } finally {
            synchronized (outputQueue) {
                firingEvents = false;
            }
        }
         */
    }
    
    private void firePreCommitEvents(EventQueue q) {
        fireCommitEvents(preCommitListeners, q);
    }
    
    private void firePostCommitEvents(EventQueue what) {
        fireCommitEvents(postCommitListeners, what);
    }

    private void fireCommitEvents(Collection origListeners, EventQueue data) {
        /*
        if (origListeners == null || data == null || data.isEmpty())
            return;
        
        Collection listeners;
        
        synchronized (origListeners) {
            listeners = new Vector(origListeners);
        }
        
        Set removed = data.getRemovedElements();
        Set created = data.getCreatedElements();
        Map changed = data.getChangedElements();

        // if the lock will be released after this operation, fire summary events
        // to watchers as well.
        for (Iterator it = listeners.iterator(); it.hasNext(); ) {
            CommitListener l = (CommitListener)it.next();
            l.changesCommited(created, removed, changed);
        }
         */
    }
    
    private EventQueue mergeEventQueues() {
        /*
        EventQueue parent = eventQueue.getParent();
        if (parent != null) {
            if (transactionCommited) {
                eventQueue.mergeToParent();
            } else {
                // PENDING: cancel ordinary events in the queue,
                // backfire all vetoable changes.
            }
            eventQueue = parent;
        }
        transactionCommited = false;
        return eventQueue;
         */
        return null;
    }
    
    public void commitChanges() {
        /*
        if (locked == null || this.writingThread != Thread.currentThread()) {
            throw new IllegalStateException("Sanity check: commit outside lock"); // NOI18N
        }
        if (masterLocks > 0 && masterLocks < writeLocks)
            return;
        this.transactionCommited = true;
         */
    }
    
    public void runAtomic(Runnable r) throws SourceException {
        Object token = writeLock();
        try {
            r.run();
            commitChanges();
        } finally {
            releaseWriteLock(token);
        }
    }
    
    protected boolean isConstrained() {
        return this.transactionConstrained;
    }
    
    protected final org.openide.nodes.Node.Cookie findElementCookie(Element el, Class clazz) {
        return env.findCookie(el, clazz);
    }

    /**
     * Enters a special locked mode with disabled constraint and veto
     * checking.
     */
    public boolean runUpdate(Runnable r, boolean disableConstraints) 
    throws SourceException {
        Object token = tryWriteLock(true);
        if (token == null)
            return false;
        boolean saveConstraint = this.transactionConstrained;
        
        try {
            this.transactionConstrained = !disableConstraints;
            r.run();
            /*
        } catch (SourceException ex) {
            throw ex;
             */
            commitChanges();
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new SourceException("Unexpected implementation error"); // NOI18N
        } finally {
            this.transactionConstrained = saveConstraint;
            releaseWriteLock(token, true);
        }
    }
    
    private void doRunAtomic(ExceptionRunnable r) throws SourceException {
        // nothing done here (for now).
        try {
            r.run();
        } catch (SourceException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new SourceException("Unexpected implementation error."); // NOI18N
        }
    }
    
    public void runAtomic(ExceptionRunnable r) throws SourceException {
        boolean fire = false;
        boolean ok = false;
        Object token = null;

        token = writeLock();
        try {
            doRunAtomic(r);
            commitChanges();
        } finally {
            releaseWriteLock(token);
        }
    }
    
    public Object getManagementLock() {
        return writerNotify;
    }
    
    public final void readLock() {
        /*
        synchronized (writerNotify) {
            if (writingThread != null) {
                if (writingThread == Thread.currentThread()) {
                    readerCount++;
                    return;
                }
            }
            try {
                writerNotify.wait();
            } catch (InterruptedException ex) {
                // PENDING: rethrow another (Runtime) exception.
            }
            if (locked == null) {
                locked = Thread.currentThread();
            }
            readerCount++;
        }
         */
    }

    public final void releaseReadLock() {
        /*
        synchronized (writerNotify) {
            if (--readerCount == 0) {
                if (writingThread == null) {
                    locked = null;
                    writerNotify.notifyAll();
                }
            }
        }
         */
    }
    
    protected EventQueue getEventQueue() {
        return this.eventQueue;
    }
    
    public boolean isWriteLocked() {
        return this.writingThread != null;
    }
    
    public Item createImport(Import im, int from, int to) {
        return null;
    }        
    
    public void notifyElementChanged(Element ref, Element old) {
        eventQueue.elementChanged(ref, old);
    }
    
    public void notifyElementCreated(Element ref) {
        eventQueue.elementCreated(ref);
    }
    
    public void notifyElementRemoved(Element ref) {
        eventQueue.elementRemoved(ref);
    }
    
    public void fireModelElementChange(ElementImpl bean, PropertyChangeEvent evt) {
        queuePropertyChange(bean, evt.getPropertyName(), evt);
    }
    
    public void fireModelElementChange(Element.Impl bean, PropertyChangeEvent evt) {
        queuePropertyChange((ElementImpl)bean, evt.getPropertyName(), evt);
    }
    
    // Model-private protocol
    ///////////////////////////////////////////////////////////////////////////////

    protected BindingFactory getBindingFactory() {
        if (this.bindingFactory != null) {
            return this.bindingFactory;
        }
        return this.bindingFactory = env.getBindingFactory();
    }
    
    public WrapperFactory getWrapper() {
        if (this.wrapperFactory != null) {
            return this.wrapperFactory;
        }
        synchronized (this) {
            if (this.wrapperFactory == null) {
                this.wrapperFactory = env.getWrapperFactory();
            }
            return this.wrapperFactory;
        }
    }
    
    // Implementation:
    ///////////////////////////////////////////////////////////////////////////////
    private void queuePropertyChange(ElementImpl bean, String name, PropertyChangeEvent evt) {
        eventQueue.addPropertyChange(bean, evt);
    }
    
    private ElementImpl getElementImpl(Element el) {
        return (ElementImpl)el.getCookie(ElementImpl.class);
    }
    
    public void updateMembers(Element target, String propertyName, Element[] els, 
        int[] orderIndices,
        int[] optMap) {
        if (target instanceof ClassElement) {
            ClassElementImpl impl = (ClassElementImpl)getElementImpl(target);
            impl.updateMembers(propertyName, els, orderIndices, optMap);
        } else {
            SourceElementImpl impl = (SourceElementImpl)getElementImpl(target);
            impl.updateMembers(propertyName, els, optMap);
        }
    }
    
    public void updateMemberOrder(Element target, String id, 
        Element[] orderedMembers) {
        ClassElementImpl impl = (ClassElementImpl)getElementImpl(target);
        impl.updateMemberOrder(orderedMembers);
    }
    
    public void activate(Element target) {
        ElementImpl impl = getElementImpl(target);
        impl.notifyCreate();
    }
    
    public Binding getElementBinding(Element target) {
        ElementImpl impl = getElementImpl(target);
        return impl.getRawBinding();
    }
    
    public boolean isSameContext(Element context, Identifier id) {
        ElementImpl impl = getElementImpl(context);
        if (impl == null)
            return false;
        return impl.checkIdentifierContext(id);
    }
    
    public Identifier createLocalIdentifier(Element context, Identifier id, int status) {
        ElementImpl impl = getElementImpl(context);
        if (impl == null)
            throw new IllegalArgumentException("Unknown context class: " + context.getClass()); // NOI18N
        return impl.createLocalIdentifier(id, status);
    }
    
    public Identifier createLocalIdentifier(Element context, String full, String source,
    int status) {
        Identifier id = Identifier.create(full, source);
        return createLocalIdentifier(context, id, status);
    }
    
    // methods related to commit listener
    
    private void doFire (Set removed, Set created, Map changed) {
        if (postCommitListeners != null) {
            MDRepository repository = JavaMetamodel.getDefaultRepository();
            repository.beginTrans(false);
            try {
                synchronized (postCommitListeners) {
                    for (Iterator it = postCommitListeners.iterator(); it.hasNext();) {
                        CommitListener l = (CommitListener)it.next();
                        l.changesCommited(created, removed, changed);
                    }
                }
            } finally {
                repository.endTrans(false);
            }
        }
    }
    
    public void fireElementChanged (Element oldElem, Element newElem) {
        Map changed = new HashMap ();
        changed.put (newElem, new Element [] {oldElem, newElem});
        
        doFire (new HashSet (), new HashSet (), changed);
    }

    public void fireElementRemoved (Element elem) {
        Set removed = new HashSet ();
        removed.add (elem);        
        
        doFire (removed, new HashSet (), new HashMap ());
    }
    
    public void fireElementAdded (Element elem) {        
        Set created = new HashSet ();
        created.add (elem);
             
        doFire (new HashSet (), created, new HashMap ());
    }
     
    public void fireElementSet (Element oldElem, Element newElem) {
        Set removed = new HashSet ();
        removed.add (oldElem);
        Set created = new HashSet ();
        created.add (newElem);
         
        doFire (removed, created, new HashMap ());
    }
    
    public Element findElement(Element.Impl impl) {
        if (!(impl instanceof ElementImpl))
            return null;
        return ((ElementImpl)impl).getElement();
    }
    
    public void firePropertyChange(Element el, PropertyChangeEvent evt) {
        ElementImpl impl = getElementImpl(el);
        if (impl == null) {
            // [PENDING]
            JMManager.getLog().log(ErrorManager.INFORMATIONAL, "DefaultLangModel.firePropertyChange(): impl == null"); // NOI18N
            return;
        }
        impl.fireOwnPropertyChange(evt);
    }
    
    public void updateBody(Element el, String bodyContent) throws UnsupportedOperationException {
        /*
        ElementImpl impl = getElementImpl(el);
        if (impl instanceof CallableImpl) 
            ((CallableImpl)impl).updateBody(bodyContent);
        else  if (impl instanceof InitializerElementImpl)
            ((InitializerElementImpl)impl).updateBody(bodyContent);
        else
            throw new UnsupportedOperationException();
         */
    }
 
    public void invalidateModel(SourceElement el) {
        SourceElementImpl impl = (SourceElementImpl)getElementImpl(el);
        Object token = writeLock();
        try {
            if (impl == null)
                return;
            impl.notifyRemove();
            commitChanges();
        } finally {
            releaseWriteLock(token);
        }
    }
}
... 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.