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.xml.tree.completion;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.LinkedList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;

import org.netbeans.tax.*;
import org.netbeans.tax.dom.Wrapper;
import org.netbeans.spi.xml.cookies.DataObjectAdapters;
import org.netbeans.modules.xml.core.XMLDataObject;
import org.netbeans.modules.xml.api.model.GrammarEnvironment;
import org.netbeans.modules.xml.api.model.GrammarQuery;
import org.netbeans.modules.xml.api.model.GrammarQueryManager;
import org.netbeans.modules.xml.spi.model.EmptyQuery;
import org.netbeans.modules.xml.tax.cookies.TreeEditorCookie;

import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.util.RequestProcessor;
import org.openide.util.Task;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.openide.awt.StatusDisplayer;

/**
 * Manages grammar to tree editor association.
 *
 * Mostly copied from the xml-text editor's version, but
 * this one cannot dynamically update.
 *
 * @author  Rich Unger 
 */
public class GrammarManager {

    // maintain a map of DataObject --> Grammar
    private static HashMap s_map = new HashMap();

    // last invalidation time
    private long timestamp = System.currentTimeMillis();
    private int  delay = 0;

    // current cache state
    private int state = INVALID;

    static final int VALID = 1;
    static final int LOADING = 2;
    static final int INVALID = 3;

    // cache entry
    private GrammarQuery grammar;  

    // noop loader
    private static final RequestProcessor.Task EMPTY_LOADER =
        RequestProcessor.createRequest(Task.EMPTY);

    // current loader
    private RequestProcessor.Task loader = EMPTY_LOADER;

    // grammar is provided for this document
    private final XMLDataObject dObj;

    public static GrammarQuery getGrammar( XMLDataObject d )
    { 
        if (d == null) throw new IllegalArgumentException("null");              // NOI18N
        
        synchronized( s_map )
        { 
            GrammarManager mgr = (GrammarManager)s_map.get(d);
            if( mgr == null )
            { 
                mgr = new GrammarManager(d);
                s_map.put(d, mgr);
            } 
            return mgr.getGrammar(1000);
        } 
    } 

    /**
     * Create new manager.
     */
    public GrammarManager(XMLDataObject dObj) {
        if (dObj == null) throw new IllegalArgumentException("null");           // NOI18N
        this.dObj = dObj;
    }
    
    /**
     * Return any suitable grammar that you can get 
     * till expires given timeout.
     */
    public synchronized GrammarQuery getGrammar(int timeout) {

        switch (state) {
            case VALID:
                return grammar;

            case INVALID:
                state = LOADING;
                loadGrammar();  // async

            case LOADING:
                waitLoaded(timeout); // possible thread switch !!!

                //??? return last loaded grammar (use option?)
                if (grammar != null) return grammar;

            default:                    
                return EmptyQuery.INSTANCE;
        }
    }

    /**
     * Nofification from grammar loader thread, new valid grammar.
     * @param grammar grammar or null if cannot load.
     */
    private synchronized void grammarLoaded(Task loader, GrammarQuery grammar) {

        try {
            // eliminate zombie loader
            if (this.loader != loader) return;

            String status = (grammar != null) ? Util.THIS.getString("MSG_loading_done") 
                : Util.THIS.getString("MSG_loading_failed");

            this.grammar = grammar == null ? EmptyQuery.INSTANCE : grammar;
            state = VALID;

            notifyProgress(loader, status);            
        } finally {
            notifyAll();
        }
    }

    /**
     * Notify loader progress filtering out messages from zombies
     */
    private void notifyProgress(Task loader, String msg) {
        if (this.loader != loader) return;
        StatusDisplayer.getDefault().setStatusText(msg);
    }

    /**
     * Async grammar fetching
     */
    private void loadGrammar() {

        class LoaderTask extends Task {

            // my represenetation in RQ as others see it
            private RequestProcessor.Task self;

            public void run() {

                GrammarQuery loaded = null;                    
                try {

                    String status = Util.THIS.getString("MSG_loading");
                    notifyProgress(self, status);

                    GrammarQueryManager gqm = GrammarQueryManager.getDefault();

                    InputSource inputSource = DataObjectAdapters.inputSource(dObj);
                    FileObject fObj = dObj.getPrimaryFile();

                    // get the document children
                    TreeDocumentRoot docRoot = null;
                    try {
                        TreeDocumentRoot result;

                        TreeEditorCookie cake = (TreeEditorCookie) dObj.getCookie(TreeEditorCookie.class);
                        if (cake != null) {
                            result = cake.openDocumentRoot();
                        } else {
                            throw new TreeException("XMLDataObject:INTERNAL ERROR"); // NOI18N
                        }
                        docRoot = result;
                    } 
                    catch( Exception ex ) { 
                        return;
                    } 

                    Iterator treeObjectIter = docRoot.getChildNodes().iterator();

                    LinkedList ctx = new LinkedList ();

                    while( treeObjectIter.hasNext() )
                    { 
                        TreeObject child = (TreeObject)treeObjectIter.next();
                        try { 
                            ctx.add ( Wrapper.wrap(child) );
                        } 
                        catch( RuntimeException ex ) { 
                            System.err.println("Could not wrap TAX object: " 
                                    + child.getClass().getName());
                        } 
                    } 

                    GrammarEnvironment env = new GrammarEnvironment(Collections.enumeration (ctx), inputSource, fObj);

                    if( gqm.enabled(env) != null )
                    { 
                        loaded = gqm.getGrammar(env);
                    }

                } finally {
                    grammarLoaded(self, loaded);
                    notifyFinished();
                }

            }
        }

        // we need a fresh thread per loader (some requests may block)
        RequestProcessor rp = RequestProcessor.getDefault();
        LoaderTask task = new LoaderTask();
        loader = rp.create(task);
        task.self = loader;

        // do not allow too many loaders if just editing invalidation area
        loader.schedule(delay);
    }

    /**
     * Wait till grammar is loaded or given timeout expires
     */
    private void waitLoaded(int timeout) {
        try {
            if (state == LOADING) wait(timeout);
        } catch (InterruptedException ex) {
        }
    }

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