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-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.java;

import java.beans.*;
import java.io.*;
import java.util.*;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
import javax.swing.SwingUtilities;

import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Segment;
import javax.swing.text.StyledDocument;
import org.netbeans.jmi.javamodel.Feature;
import org.netbeans.jmi.javamodel.JavaDoc;

import org.openide.util.Task;
import org.openide.cookies.*;
import org.openide.loaders.MultiDataObject;
import org.openide.loaders.DataObject;
import org.openide.nodes.Node;
import org.openide.nodes.CookieSet;
import org.openide.src.*;
import org.openide.text.*;
import org.openide.util.WeakSet;
import org.openide.util.RequestProcessor;

import org.netbeans.modules.java.parser.LangEnvImpl;
import org.netbeans.modules.java.parser.JavaParser;
import org.netbeans.modules.java.codegen.DocumentBinding;
import org.netbeans.modules.java.codegen.TextBinding;

import org.netbeans.modules.java.bridge.BindingFactory;
import org.netbeans.modules.java.bridge.WrapperFactory;
import org.netbeans.modules.java.bridge.LangModel;
import org.netbeans.modules.java.bridge.ElementOrder;
import org.netbeans.modules.java.bridge.ElementImpl;

import org.netbeans.modules.java.parser.ParsingSupport;
import org.netbeans.modules.java.codegen.SourceText;
import org.netbeans.modules.java.bridge.DefaultLangModel;

import org.netbeans.modules.java.codesync.SynchronizeCodeCookie;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;


/**
 * A glue between a JavaDataObject and JavaParser
 * 
 * @author  sdedic
 * @version 0.1
 */
class JavaParserGlue implements JavaParser.Env, DocumentBinding.Env, 
    LangModel.Env, SourceCookie.Editor {

    private MultiDataObject.Entry   sourceEntry;
    private JavaDataObject           jdo;
    private DocumentBinding         docBinding;
    private LangEnvImpl             envSupport;
    private ParsingSupport          parser;
    private Map                     cookieMap;
    private SiblingListener         l;
    private boolean                 documentLoaded;
    private CloneableEditorSupport  cloneableEditSupp;
    private int                     suspended;
    private PropertyChangeListener  weakPropListener;
    private boolean                 editorBound;
    
    /**
     * Constructs an implementation of a parsing environment and binds it to the
     * data object.
     */
    public JavaParserGlue(MultiDataObject.Entry en) {
        this.sourceEntry = en;
        this.jdo = (JavaDataObject)en.getDataObject();
        this.docBinding = new SourceText(this);
        this.envSupport = new LangEnvImpl(docBinding);
        this.cookieMap = new WeakHashMap(57);
        
        DefaultLangModel model = new DefaultLangModel(this, jdo);
        envSupport.setModel(model);
        
        ParsingSupport supp = new ParsingSupport(this, jdo, docBinding, model, model);
        parser = supp;
        l = new SiblingListener();
        parser.getSource().addPropertyChangeListener(l);
        jdo.addPropertyChangeListener(l);
        // 
        if (parser.getStatus() != SourceElement.STATUS_NOT)
            l.rescanSource(parser.getSource());
    }
    
    /**
     * The implementation provides custom OpenCookies that open the JavaEditor
     * associated with the JavaDataObject at the where the Element starts.
     */
    public Node.Cookie findCookie(Element el, Class clazz) {
        Node.Cookie lookup = null;

        if (el == parser.getSource())
            // handle it specially for SourceElement - it has much more common with DataObject than
            // the rest of Elements.
            return findCookie((SourceElement)el, clazz);
        if (SynchronizeCodeCookie.class == clazz) {
            // disable, if r/o
            if (jdo.getPrimaryFile().isReadOnly())
                return null;
            
            lookup = lookupCookie(el, clazz);
            if (lookup != null)
                return lookup;
            
            if (el instanceof ClassElement) {
                lookup = createClassSyncCookie((ClassElement)el);
            } else if (el == parser.getSource()) {
                return jdo.connectionManager;
            }
        } else if (clazz == OpenCookie.class) {
            lookup = lookupCookie(el, clazz);
            if (lookup != null)
                return lookup;
            lookup = createOpenCookie(el);
        } else  {
            return jdo.getCookie(clazz);
        }
        return lookup;
    }
    
    public void annotateThrowable(Throwable t, String locMessage, boolean user) {
        Util.annotateThrowable(t, locMessage, user);
    }
    
    public void annotateThrowable(Throwable wrapper, Throwable nested) {
        Util.annotateThrowable(wrapper, nested);
    }
    
    public Node.Cookie findCookie(SourceElement src, Class clazz) {
        return jdo.getCookie(clazz);
    }
    
    /**
     * Notifies the mediator that a CloneablEditorSupport has been created.
     */
    public void cloneableSupportCreated(CloneableEditorSupport supp) {
        bindEditorSupport(supp);
    }
    
    public JavaParser getParser() {
        return parser;
    }

    public org.openide.filesystems.FileObject getSourceFile() {
        return jdo.getPrimaryFile();
    }

    public String getSourceName() {
        return jdo.getPrimaryFile().getNameExt();
    }

    /**
     * Environment can find precompiled classes to speed up name resoltion
     */
    public InputStream findCompiledClass(String classFQN) {
        // PENDING: migrate code from the old V8ParseRequest
        return null;
    }
    
    /** 
     * The implementation first tries to get a document - if it is already opened.
     * If it is, then it returns a CharArrayReader constructed on the document's
     * contents. Document is read using {@link StyledDocument#render} to avoid
     * data corruption.

* If the document is not (yet) opened, a {@link JavaEditor#GuardedReader} is * constructed so the output will not contain any guarded block information. *

* The implementation does not handle IOException from lower layers in any * way and passes them to the caller. */ public Reader getSourceText() throws IOException { CloneableEditorSupport je = findEditorSupport(); final StyledDocument doc = je.getDocument(); if (doc != null) { // can read from the document! final Segment s = new javax.swing.text.Segment(); doc.render(new Runnable() { public void run() { try { doc.getText(0, doc.getLength(), s); } catch (BadLocationException ex) { // should never happen } } }); return new CharArrayReader(s.array, s.offset, s.count); } else { JavaDataLoader.JavaFileEntry en = (JavaDataLoader.JavaFileEntry)sourceEntry; return new JavaEditor.GuardedReader(en.getInputStream(), true, Util.getFileEncoding(en.getFile())); } } protected Node.Cookie createClassSyncCookie(Element el) { return jdo.connectionManager.createClassSyncCookie((ClassElement)el); } /** * Creates an OpenCookie for the specified element. The cookies created are * kept in caching WeakHashMap keyed by the element instances for reuse. */ protected OpenCookie createOpenCookie(Element el) { ElementImpl impl = (ElementImpl)el.getCookie(ElementImpl.class); OpenCookie ck = new OpenCookieImpl((TextBinding)impl.getBinding()); return ck; } private Node.Cookie lookupCookie(Element el, Class clazz) { synchronized (cookieMap) { Object o = cookieMap.get(el); if (o == null) return null; if (o instanceof CookieSet) return ((CookieSet)o).getCookie(clazz); if (o.getClass().isAssignableFrom(clazz)) return (Node.Cookie)o; return null; } } private TextBinding getBinding(Element el) { ElementImpl impl = (ElementImpl)el.getCookie(ElementImpl.class); if (impl == null) { Element.Impl iimpl = (Element.Impl)el.getCookie(Element.Impl.class); throw new IllegalArgumentException("Incompatible implementation: " + // NOI18N iimpl); } return (TextBinding)impl.getBinding(); } // ============== Implementation of DocumentBinding.Env ================== public CloneableEditorSupport findEditorSupport() { if (this.cloneableEditSupp == null) { bindEditorSupport(jdo.findCloneableEditorSupport()); } return cloneableEditSupp; } public void takeLock() throws IOException { jdo.getPrimaryEntry().takeLock(); } private synchronized void bindEditorSupport(CloneableEditorSupport supp) { if (!editorBound) { supp.addChangeListener(l); documentLoaded = supp.isDocumentLoaded(); editorBound = true; cloneableEditSupp = supp; if (documentLoaded) l.stateChanged(new ChangeEvent(supp)); } } public PositionRef findFreePosition(PositionBounds bounds) { return getJavaEditor().findFreePosition(bounds); } private JavaEditor getJavaEditor() { return (JavaEditor)jdo.getCookie(JavaEditor.class); } // ============== Implementation of LangModel.Env -- DELEGATING ================== /** * Returns the document binding as the binding factory. */ public BindingFactory getBindingFactory() { return envSupport.getBindingFactory(); } public WrapperFactory getWrapperFactory() { return envSupport.getWrapperFactory(); } public void complete(Element scope, int informationKind) { envSupport.complete(scope, informationKind); } public Type resolveType(Element context, Type original) { return envSupport.resolveType(context, original); } public Identifier resolveTypeIdent(Element context, Identifier original) { return envSupport.resolveTypeIdent(context, original); } /** * Implementation of SourceCookie. */ public SourceElement getSource() { return parser.getSource(); } /** * Implementation of OpenCookie available on individual elements. */ private class OpenCookieImpl implements OpenCookie, Runnable { TextBinding binding; OpenCookieImpl(TextBinding binding) { this.binding = binding; } public void open() { // Fix #20551: if the thread is not the event one, replan // the open action into the AWT thread. org.openide.util.Mutex.EVENT.postReadRequest(this); } public void run() { PositionBounds elBounds = binding.getElementRange(true); getJavaEditor().openAt(elBounds.getBegin()); } } public StyledDocument getDocument() { return findEditorSupport().getDocument(); } public Line.Set getLineSet() { return findEditorSupport().getLineSet(); } public javax.swing.JEditorPane[] getOpenedPanes() { return findEditorSupport().getOpenedPanes(); } public boolean isModified() { return findEditorSupport().isModified(); } public void open() { findEditorSupport().open(); } public StyledDocument openDocument() throws IOException { return findEditorSupport().openDocument(); } public Task prepareDocument() { return findEditorSupport().prepareDocument(); } public void saveDocument() throws IOException { findEditorSupport().saveDocument(); } public boolean close() { return findEditorSupport().close(); } public void suspendDocumentChanges() { suspended++; } public void resumeDocumentChanges() { --suspended; } protected boolean isDocumentSuspended() { return suspended > 0; } private void dissolve() { suspendDocumentChanges(); jdo.removePropertyChangeListener(l); // callback to JavaDataObject (disable rest of the system). jdo.suspendSupports(); synchronized (this) { if (cloneableEditSupp != null) { cloneableEditSupp.removeChangeListener(l); editorBound = false; StyledDocument d = cloneableEditSupp.getDocument(); if (d != null) d.removeDocumentListener(l); } } parser.invalidate(); } private class SiblingListener implements PropertyChangeListener, ChangeListener, DocumentListener { private StyledDocument doc; private Reference mainClass; private Set knownClasses; SiblingListener() { knownClasses = new WeakSet(); } public void propertyChange(PropertyChangeEvent p) { String evName = p.getPropertyName(); Object source = p.getSource(); if (source == jdo) dataObjectPropertyChange(evName, p); else if (source instanceof ClassElement) { classPropertyChange(evName, (ClassElement)source, p); } else if (source instanceof SourceElement) { sourcePropertyChange(evName, p); } } private void classPropertyChange(String name, ClassElement c, PropertyChangeEvent e) { if (ElementProperties.PROP_VALID.equals(name)) { c.removePropertyChangeListener(this); } else if (ElementProperties.PROP_NAME.equals(name)) { if (mainClass == null || mainClass.get() != c) { // some other class is renamed. rescanSource(c.getSource()); } else { // rename the dataobject, if possible. String n = jdo.getName(); String n2 = (String)((Identifier)e.getNewValue()).getName(); if (!n.equals(n2)) { try { jdo.rename(n2); } catch (IOException ex) { // swallow. rescanSource(c.getSource()); } } } } else if (ElementProperties.PROP_MODIFIERS.equals(name)) { // public class may loose its modifiers -> some other class // may become the main one. non-public class may become public rescanSource(c.getSource()); } } private void sourcePropertyChange(String name, PropertyChangeEvent e) { if (ElementProperties.PROP_CLASSES.equals(name)) { rescanSource((SourceElement)e.getSource()); } else if (ElementProperties.PROP_STATUS.equals(name)) { Integer i = (Integer)e.getOldValue(); if (i == null || i.intValue() != SourceElement.STATUS_NOT) return; if (e.getNewValue() != null && ((Integer)e.getNewValue()).intValue() != SourceElement.STATUS_OK) return; rescanSource((SourceElement)e.getSource()); } } private void rescanSource(SourceElement el) { ClassElement[] classes = el.getClasses(); String currentName = jdo.getName(); ClassElement main = null; // sweep through all top-level classes. for (int i = 0; i < classes.length; i++) { ClassElement c = classes[i]; String clName = c.getName().getName(); if (knownClasses.add(c)) { c.addPropertyChangeListener(this); } if (main == null && clName.equals(jdo.getName()) && (c.getModifiers() & Modifier.PUBLIC) > 0) { main = c; } } mainClass = main == null ? null : new WeakReference(main); } private void dataObjectPropertyChange(String evName, final PropertyChangeEvent p) { if (DataObject.PROP_MODIFIED.equals(evName) && !jdo.isModified()) { // after save of the object, or reload. // if (jdo.isValid()) // parser.parse(parser.PRIORITY_NORMAL, false, true); } else if (DataObject.PROP_VALID.equals(evName) && ((Boolean)p.getNewValue()).booleanValue() == false) { dissolve(); } else if (DataObject.PROP_NAME.equals(evName)) { // SourceElement.Impl impl = parser.getSourceImpl(); // SourceElement el = parser.getSource(); // // if (impl != null) { // rescanSource(el); // } } } public void stateChanged(ChangeEvent e) { // document state change CloneableEditorSupport supp = (CloneableEditorSupport)e.getSource(); StyledDocument d = supp.getDocument(); if (d != null) { if (doc == null) { // parser.parse(parser.PRIORITY_NORMAL, false, true); synchronized (this) { doc = d; d.addDocumentListener(this); documentLoaded = true; } } } else { removeDocListener(); } } private synchronized void removeDocListener() { if (doc != null) doc.removeDocumentListener(this); doc = null; documentLoaded = false; } public void removeUpdate(javax.swing.event.DocumentEvent p1) { documentChanged(); } public void changedUpdate(javax.swing.event.DocumentEvent p1) { // text is not modified, so we can ignore this event } public void insertUpdate(javax.swing.event.DocumentEvent p1) { documentChanged(); } private void documentChanged() { if (!isDocumentSuspended()) { parser.sourceChanged(-1, -1); getJavaEditor().restartTimer(false); } } } private Map swingElementMap; // SourceCookie.Editor - specifics public org.openide.src.Element findElement(int offset) { javax.swing.text.Element swingEl = sourceToText(getSource()); while (swingEl != null) { if (swingEl.isLeaf()) { return ((TextElement)swingEl).getSourceElement(); } int elIndex = swingEl.getElementIndex(offset); if (elIndex == -1) return ((TextElement)swingEl).getSourceElement(); swingEl = swingEl.getElement(elIndex); } return null; } public org.openide.src.Element textToSource(javax.swing.text.Element element) throws NoSuchElementException { if (!(element instanceof TextElement)) { throw new NoSuchElementException(); } TextElement el = (TextElement)element; return el.getSourceElement(); } /** * Returns null, if the underlying document has not yet been loaded */ public javax.swing.text.Element sourceToText(org.openide.src.Element element) { javax.swing.text.Document d; try { d = findEditorSupport().openDocument(); } catch (IOException ex) { IllegalStateException x = new IllegalStateException("Could not load document"); // NOI18N org.openide.ErrorManager.getDefault().annotate(x, ex); throw x; } if (swingElementMap == null) { synchronized (this) { if (swingElementMap == null) swingElementMap = new WeakHashMap(75); } } Reference r = (Reference)swingElementMap.get(element); javax.swing.text.Element e = r == null ? null : (javax.swing.text.Element)r.get(); if (e == null) { e = new TextElement(element); synchronized (this) { swingElementMap.put(element, new WeakReference(e)); } } return e; } private static final Element[] NO_CHILDREN = new Element[0]; private class TextElement implements TextBinding.ExElement { private Element myElement; private org.netbeans.jmi.javamodel.Element refObject; public TextElement(Element element) { myElement = element; ElementImpl impl = (ElementImpl) myElement.getCookie(ElementImpl.class); if (impl != null) { refObject = (org.netbeans.jmi.javamodel.Element)impl.getJavaElement (); } } private PositionBounds getBounds() { try { JavaMetamodel manager=JavaMetamodel.getManager(); PositionBounds bounds=manager.getElementPosition(refObject); if (refObject instanceof Feature) { Feature f=(Feature)refObject; JavaDoc jdoc=f.getJavadoc(); if (jdoc!=null) { PositionBounds jbounds=manager.getElementPosition(jdoc); PositionRef begin=jbounds.getBegin(); PositionRef end=bounds.getEnd(); return new PositionBounds(begin, end); } } return bounds; } catch (javax.jmi.reflect.InvalidObjectException e) { return null; } } public PositionRef getDeclarationStart() { try { PositionBounds bounds = JavaMetamodel.getManager().getElementPosition(refObject); if (bounds == null) return null; return bounds.getBegin (); } catch (javax.jmi.reflect.InvalidObjectException e) { return null; } } private Element[] getChildrenElements() { ElementOrder ck = (ElementOrder)myElement.getCookie( ElementOrder.class); if (ck == null) return NO_CHILDREN; return ck.getElements(); } public int getElementIndex(int offset) { Element[] children = getChildrenElements(); javax.swing.text.Element childElement; for (int i = 0; i < children.length; i++) { childElement = sourceToText(children[i]); if (childElement.getStartOffset() <= offset && childElement.getEndOffset() >= offset) { return i; } } return -1; } public javax.swing.text.AttributeSet getAttributes() { return null; } public Element getSourceElement() { return myElement; } public javax.swing.text.Document getDocument() { return JavaParserGlue.this.getDocument(); } public javax.swing.text.Element getElement(int index) { Element[] els = getChildrenElements(); if (index > els.length) throw new IllegalArgumentException(); return sourceToText(els[index]); } public int getElementCount() { return getChildrenElements().length; } public int getEndOffset() { PositionBounds bounds = getBounds(); return bounds != null ? getBounds().getEnd().getOffset() - 1 : 0; // [PENDING] } public String getName() { return myElement.getClass().getName(); } public javax.swing.text.Element getParentElement() { Element parent; if (myElement instanceof MemberElement) { parent = ((MemberElement)myElement).getDeclaringClass(); if (parent == null && myElement instanceof ClassElement) { parent = ((ClassElement)myElement).getSource(); } } else if (myElement instanceof InitializerElement) { parent = ((InitializerElement)myElement).getDeclaringClass(); } else return null; return sourceToText(parent); } public int getStartOffset() { PositionBounds bounds = getBounds(); return bounds != null ? bounds.getBegin().getOffset() : 0; } public boolean isLeaf() { return getChildrenElements().length == 0; } } }

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