EditorModule.java :  » IDE-Netbeans » editor » org » netbeans » modules » editor » Java Open Source

Java Open Source » IDE Netbeans » editor 
editor » org » netbeans » modules » editor » EditorModule.java
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 */

package org.netbeans.modules.editor;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.text.EditorKit;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.rtf.RTFEditorKit;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.editor.AnnotationType;
import org.netbeans.editor.AnnotationTypes;
import org.netbeans.editor.BaseKit;
import org.netbeans.editor.FindSupport;
import org.netbeans.editor.FindSupport.SearchPatternWrapper;
import org.netbeans.editor.LocaleSupport;
import org.netbeans.editor.Settings;
import org.netbeans.modules.editor.lib2.EditorApiPackageAccessor;
import org.netbeans.modules.editor.options.AllOptions;
import org.netbeans.modules.editor.options.AllOptionsFolder;
import org.netbeans.modules.editor.options.AnnotationTypesFolder;
import org.netbeans.modules.editor.options.BaseOptions;
import org.netbeans.swing.tabcontrol.TabbedContainer;
import org.openide.cookies.EditorCookie;
import org.openide.modules.ModuleInstall;
import org.openide.nodes.Node;
import org.openide.text.CloneableEditor;
import org.openide.text.CloneableEditorSupport;
import org.openide.util.Lookup;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.openidex.search.SearchHistory;
import org.openidex.search.SearchPattern;

/**
 * Module installation class for editor.
 *
 * @author Miloslav Metelka
 */
public class EditorModule extends ModuleInstall {

    private static final Logger LOG = Logger.getLogger(EditorModule.class.getName());
    
    private static final boolean debug = Boolean.getBoolean("netbeans.debug.editor.kits");

    private PropertyChangeListener searchSelectedPatternListener;
    private PropertyChangeListener editorHistoryChangeListener;
    private PropertyChangeListener topComponentRegistryListener;

    /** Module installed again. */
    public @Override void restored () {
        LocaleSupport.addLocalizer(new NbLocalizer(AllOptions.class));
        LocaleSupport.addLocalizer(new NbLocalizer(BaseKit.class));

        // register loader for annotation types
        AnnotationTypes.getTypes().registerLoader( new AnnotationTypes.Loader() {
                public void loadTypes() {
                    AnnotationTypesFolder.getAnnotationTypesFolder();
                }
                public void loadSettings() {
                    // AnnotationType properties are stored in BaseOption, so let's read them now
                    BaseOptions bo = (BaseOptions)BaseOptions.findObject(BaseOptions.class, true);

                    Integer i = (Integer)bo.getSettingValue(AnnotationTypes.PROP_BACKGROUND_GLYPH_ALPHA);
                    if (i != null)
                        AnnotationTypes.getTypes().setBackgroundGlyphAlpha(i.intValue());
                    Boolean b = (Boolean)bo.getSettingValue(AnnotationTypes.PROP_BACKGROUND_DRAWING);
                    if (b != null)
                        AnnotationTypes.getTypes().setBackgroundDrawing(b);
                    b = (Boolean)bo.getSettingValue(AnnotationTypes.PROP_COMBINE_GLYPHS);
                    if (b != null)
                        AnnotationTypes.getTypes().setCombineGlyphs(b);
                    b = (Boolean)bo.getSettingValue(AnnotationTypes.PROP_GLYPHS_OVER_LINE_NUMBERS);
                    if (b != null)
                        AnnotationTypes.getTypes().setGlyphsOverLineNumbers(b);
                    b = (Boolean)bo.getSettingValue(AnnotationTypes.PROP_SHOW_GLYPH_GUTTER);
                    if (b != null)
                        AnnotationTypes.getTypes().setShowGlyphGutter(b);
                }
                public void saveType(AnnotationType type) {
                    AnnotationTypesFolder.getAnnotationTypesFolder().saveAnnotationType(type);
                }
                public void saveSetting(String settingName, Object value) {
                    // AnnotationType properties are stored to BaseOption
                    BaseOptions bo = (BaseOptions)BaseOptions.findObject(BaseOptions.class, true);
                    bo.setSettingValue(settingName, value);
                }
            } );

        // ------------------------------------------------------------
        // Autoregistration
            
        // First, initialize JDK's editor kit types registry
        initAndCheckEditorKitTypeRegistry("text/plain", null); //NOI18N
        initAndCheckEditorKitTypeRegistry("text/html", HTMLEditorKit.class.getName()); //NOI18N
        initAndCheckEditorKitTypeRegistry("text/rtf", RTFEditorKit.class.getName()); //NOI18N
        initAndCheckEditorKitTypeRegistry("application/rtf", RTFEditorKit.class.getName()); //NOI18N
            
        // Now hook up to the JDK's editor kit registry
        // XXX: This all should be removed, see IZ #80110
        try {
            Field keyField = JEditorPane.class.getDeclaredField("kitRegistryKey");  // NOI18N
            keyField.setAccessible(true);
            Object key = keyField.get(JEditorPane.class);

            Class appContextClass = getClass().getClassLoader().loadClass("sun.awt.AppContext"); //NOI18N
            Method getAppContext = appContextClass.getDeclaredMethod("getAppContext"); //NOI18N
            Method get = appContextClass.getDeclaredMethod("get", Object.class); //NOI18N
            Method put = appContextClass.getDeclaredMethod("put", Object.class, Object.class); //NOI18N
            
            Object appContext = getAppContext.invoke(null);
            Hashtable kitMapping = (Hashtable) get.invoke(appContext, key);
            put.invoke(appContext, key, new HackMap(kitMapping));

// REMOVE: we should not depend on sun.* classes
//            Hashtable kitMapping = (Hashtable)sun.awt.AppContext.getAppContext().get(key);
//            sun.awt.AppContext.getAppContext().put(key, new HackMap(kitMapping));
        } catch (Throwable t) {
            if (debug) {
                LOG.log(Level.WARNING, "Can't hack in to the JEditorPane's registry for kits.", t);
            } else {
                LOG.log(Level.WARNING, "Can't hack in to the JEditorPane's registry for kits.");
            }
        }
            
        // Registration of the editor kits to JEditorPane
//        for (int i = 0; i < replacements.length; i++) {
//            JEditorPane.registerEditorKitForContentType(
//                replacements[i].contentType,
//                replacements[i].newKitClassName,
//                getClass().getClassLoader()
//            );
//        }

        // ------------------------------------------------------------
         
         searchSelectedPatternListener = new PropertyChangeListener(){
             
             public void propertyChange(PropertyChangeEvent evt){
                 if (evt == null){
                     return;
                 }

                 FindSupport fs = FindSupport.getFindSupport();                 
                 if (SearchHistory.LAST_SELECTED.equals(evt.getPropertyName())){
                     //System.out.println("API -> editor:");                
                     SearchPattern sp = SearchHistory.getDefault().getLastSelected();
                     if (sp==null){
                         return;
                     }

                     FindSupport.SearchPatternWrapper spw = new FindSupport.SearchPatternWrapper(sp.getSearchExpression(),
                             sp.isWholeWords(), sp.isMatchCase(), sp.isRegExp());
                     //System.out.println("spw:"+spw);
                     fs.setLastSelected(spw);
                 } else if (SearchHistory.ADD_TO_HISTORY.equals(evt.getPropertyName())){
                     List searchPatterns = SearchHistory.getDefault().getSearchPatterns();

                     List history = new ArrayList();
                     for (int i = 0; i<searchPatterns.size(); i++){
                         SearchPattern sptr = ((SearchPattern)searchPatterns.get(i));
                         SearchPatternWrapper spwrap = new SearchPatternWrapper(sptr.getSearchExpression(),
                                 sptr.isWholeWords(), sptr.isMatchCase(), sptr.isRegExp());
                         history.add(spwrap);
                     }

                     fs.setHistory(history);
                 }
             }
         };
         
         editorHistoryChangeListener =  new PropertyChangeListener(){
             public void propertyChange(PropertyChangeEvent evt){
                 
                 if (evt == null || !FindSupport.FIND_HISTORY_PROP.equals(evt.getPropertyName())){
                     return;
                 }
 
                 //System.out.println("");
                 //System.out.println("editor -> API");
                 
                 SearchPatternWrapper spw = (SearchPatternWrapper)evt.getNewValue();
                 SearchPattern spLast = SearchHistory.getDefault().getLastSelected();
                 
                 if (spw == null || spw.getSearchExpression()==null || "".equals(spw.getSearchExpression()) ){ //NOI18N
                     return;
                 }
                 //System.out.println("spw:"+spw);
                 
                 SearchPattern sp = SearchPattern.create(spw.getSearchExpression(),
                         spw.isWholeWords(), spw.isMatchCase(), spw.isRegExp());
                 
                 if (sp == null || (sp.equals(spLast))){
                     return;
                 }
                 
                 SearchHistory.getDefault().add(sp);
                 SearchHistory.getDefault().setLastSelected(sp);
             }
         };
 
         SearchHistory.getDefault().addPropertyChangeListener(searchSelectedPatternListener);
         FindSupport.getFindSupport().addPropertyChangeListener(editorHistoryChangeListener);
         
         // TEMP start
         /*
         final int fired[] = new int[1];
         fired[0] = 0;
         
         Timer timer;
         timer = new Timer(15000, new ActionListener(){
                 public void actionPerformed(ActionEvent e){
                    SearchPattern p;
                     p = SearchPattern.create(String.valueOf(fired[0]),false,false,false);
                     SearchHistory.getDefault().add(p);                    
                     SearchHistory.getDefault().setLastSelected(p);
                     fired[0] ++;
                 }
         });
         timer.start();
         */
         //TEMP end

         EditorApiPackageAccessor.get().setIgnoredAncestorClass(TabbedContainer.class);
         if (topComponentRegistryListener == null) {
             topComponentRegistryListener = new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent evt) {
                    if (TopComponent.Registry.PROP_TC_CLOSED.equals(evt.getPropertyName())) {
                        TopComponent tc = (TopComponent)evt.getNewValue();
                        // Limit checking to editors and multiviews only - should suffice
                        // if not then assign: doClose = true;
                        boolean doNotify = (tc instanceof CloneableEditorSupport.Pane);
                        LOG.finest("CLOSE-TC: doClose=" + doNotify + ", TC=" + tc + "\n");
                        if (doNotify) {
                            EditorApiPackageAccessor.get().notifyClose(tc);
                        }
                    }
                }
            };
            TopComponent.getRegistry().addPropertyChangeListener(topComponentRegistryListener);
         }

         if (LOG.isLoggable(Level.INFO)) {
             WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
                public void run() {
                    try {
                        Field kitsField = BaseKit.class.getDeclaredField("kits");
                        kitsField.setAccessible(true);
                        Map kitsMap = (Map) kitsField.get(null);
                        LOG.info("Number of loaded editor kits: " + kitsMap.size());
                    } catch (Exception e) {
                        // ignore
                    }
                }
            });
         }
    }

    /** Called when module is uninstalled. Overrides superclass method. */
    public @Override void uninstalled() {

        AllOptionsFolder.unregisterModuleRegListener();

        /* [TEMP]
        if (searchSelectedPatternListener!=null){
            SearchHistory.getDefault().removePropertyChangeListener(searchSelectedPatternListener);
        }
        */
         
        // unregister our registry
        try {
            Field keyField = JEditorPane.class.getDeclaredField("kitRegistryKey");  // NOI18N
            keyField.setAccessible(true);
            Object key = keyField.get(JEditorPane.class);
            
            Class appContextClass = getClass().getClassLoader().loadClass("sun.awt.AppContext"); //NOI18N
            Method getAppContext = appContextClass.getDeclaredMethod("getAppContext"); //NOI18N
            Method get = appContextClass.getDeclaredMethod("get", Object.class); //NOI18N
            Method put = appContextClass.getDeclaredMethod("put", Object.class, Object.class); //NOI18N
            Method remove = appContextClass.getDeclaredMethod("remove", Object.class, Object.class); //NOI18N
            
            Object appContext = getAppContext.invoke(null);
            Hashtable kitMapping = (Hashtable) get.invoke(appContext, key);

            if (kitMapping instanceof HackMap) {
                if (((HackMap) kitMapping).getOriginal() != null) {
                    put.invoke(appContext, key, new HackMap(kitMapping));
                } else {
                    remove.invoke(appContext, key);
                }
            }
            
// REMOVE: we should not depend on sun.* classes
//            HackMap kitMapping = (HackMap)sun.awt.AppContext.getAppContext().get(key);
//            if (kitMapping.getOriginal() != null) {
//                sun.awt.AppContext.getAppContext().put(key, kitMapping.getOriginal());
//            } else {
//                sun.awt.AppContext.getAppContext().remove(key);
//            }
        } catch (Throwable t) {
            if (debug) {
                LOG.log(Level.WARNING, "Can't release the hack from the JEditorPane's registry for kits.", t);
            } else {
                LOG.log(Level.WARNING, "Can't release the hack from the JEditorPane's registry for kits.");
            }
        }

        // #42970 - Possible closing of opened editor top components must happen in AWT thread
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {

                // issue #16110
                // close all TopComponents which contain editor based on BaseKit
                HashSet set = new HashSet();
                set.addAll(TopComponent.getRegistry().getOpened());

                for (Iterator it = set.iterator(); it.hasNext(); ) {
                    TopComponent topComp = (TopComponent)it.next();
                    // top components in which we are interested must be of type CloneableEditor
                    if (!(topComp instanceof CloneableEditor))
                        continue;
                    Node[] arr = topComp.getActivatedNodes();
                    if (arr == null)
                        continue;
                    for (int i=0; i<arr.length; i++) {
                        EditorCookie ec = (EditorCookie)arr[i].getCookie(EditorCookie.class);
                        if (ec == null)
                            continue;
                        JEditorPane[] pane = ec.getOpenedPanes();
                        if (pane == null) 
                            continue;
                        for (int j=0; j<pane.length; j++) {
                            if (pane[j].getEditorKit() instanceof BaseKit) {
                                topComp.close();
                            }
                        }
                    }
                }
                
                // Remove listening on top component registry
                if (topComponentRegistryListener != null) {
                    TopComponent.getRegistry().removePropertyChangeListener(topComponentRegistryListener);
                    topComponentRegistryListener = null;
                }
            }
        });
    }

    
    private static class HackMap extends Hashtable {
        
  private Hashtable delegate;

        HackMap(Hashtable h) {
            delegate = h;
            
            if (debug) {
                LOG.log(Level.INFO, "Original kit mappings: " + h); //NOI18N

                try {
                    Field keyField = JEditorPane.class.getDeclaredField("kitTypeRegistryKey");  // NOI18N
                    keyField.setAccessible(true);
                    Object key = keyField.get(JEditorPane.class);
                    
                    Class appContextClass = getClass().getClassLoader().loadClass("sun.awt.AppContext"); //NOI18N
                    Method getAppContext = appContextClass.getDeclaredMethod("getAppContext"); //NOI18N
                    Method get = appContextClass.getDeclaredMethod("get", Object.class); //NOI18N
                    Method put = appContextClass.getDeclaredMethod("put", Object.class, Object.class); //NOI18N

                    Object appContext = getAppContext.invoke(null);
                    Hashtable kitTypeMapping = (Hashtable) get.invoke(appContext, key);

                    if (kitTypeMapping != null) {
                        put.invoke(appContext, key, new DebugHashtable(kitTypeMapping));
                    }
                    
// REMOVE: we should not depend on sun.* classes
//                    Hashtable kitTypeMapping = (Hashtable)sun.awt.AppContext.getAppContext().get(key);
//                    if (kitTypeMapping != null) {
//                        sun.awt.AppContext.getAppContext().put(key, new DebugHashtable(kitTypeMapping));
//                    }
                } catch (Throwable t) {
                    LOG.log(Level.WARNING, "Can't hack in to the JEditorPane's registry for kit types.", t);
                }
            }
        }

        private String getKitClassName(String type) {
            try {
                Field keyField = JEditorPane.class.getDeclaredField("kitTypeRegistryKey");  // NOI18N
                keyField.setAccessible(true);
                Object key = keyField.get(JEditorPane.class);
                
                Class appContextClass = getClass().getClassLoader().loadClass("sun.awt.AppContext"); //NOI18N
                Method getAppContext = appContextClass.getDeclaredMethod("getAppContext"); //NOI18N
                Method get = appContextClass.getDeclaredMethod("get", Object.class); //NOI18N

                Object appContext = getAppContext.invoke(null);
                Hashtable kitTypeMapping = (Hashtable) get.invoke(appContext, key);

                if (kitTypeMapping != null) {
                    return (String) kitTypeMapping.get(type);
                }

// REMOVE: we should not depend on sun.* classes
//                Hashtable kitTypeMapping = (Hashtable)sun.awt.AppContext.getAppContext().get(key);
//                if (kitTypeMapping != null) {
//                    return (String)kitTypeMapping.get(type);
//                }
            } catch (Throwable t) {
                if (debug) {
                    LOG.log(Level.WARNING, "Can't hack in to the JEditorPane's registry for kit types.", t);
                } else {
                    LOG.log(Level.WARNING, "Can't hack in to the JEditorPane's registry for kit types.");
                }
            }
            
            return null;
        }
        
        public @Override Object get(Object key) {
            synchronized (Settings.class) {
            if (debug) LOG.log(Level.INFO, "HackMap.get key=" + key); //NOI18N
            
            Object retVal = null;
            
            if (delegate != null) {
                retVal = delegate.get(key);
                if (debug && retVal != null) {
                    LOG.log(Level.INFO, "Found cached instance kit=" + retVal + " for mimeType=" + key); //NOI18N
                }
            }

            if (key instanceof String) {
                String mimeType = (String) key;
                if (retVal == null || shouldUseNbKit(retVal.getClass().getName(), mimeType)) {
                    // first check the type registry
                    String kitClassName = getKitClassName(mimeType);
                    if (debug) {
                        LOG.log(Level.INFO, "Found kitClassName=" + kitClassName + " for mimeType=" + mimeType); //NOI18N
                    }

                    if (kitClassName == null || shouldUseNbKit(kitClassName, mimeType)) {
                        Object kit = findKit(mimeType);
                        if (kit != null) {
                            retVal = kit;
                            if (debug) {
                                LOG.log(Level.INFO, "Found kit=" + retVal + " in xml layers for mimeType=" + mimeType); //NOI18N
                            }
                        }
                    }
                }
            }
            
            return retVal;
            } // synchronized (Settings.class)
        }
        
        public @Override Object put(Object key, Object value) {
            synchronized (Settings.class) {
            if (debug) LOG.log(Level.INFO, "HackMap.put key=" + key + " value=" + value); //NOI18N
            
            if (delegate == null) {
                delegate = new Hashtable();
            }

            Object ret = delegate.put(key,value);
            
            if (debug) {
                LOG.log(Level.INFO, "registering mimeType=" + key //NOI18N
                    + " -> kitInstance=" + value // NOI18N
                    + " original was " + ret); // NOI18N
            }
             
            return ret;
            } // synchronized (Settings.class)
        }

        public @Override Object remove(Object key) {
            synchronized (Settings.class) {
            if (debug) LOG.log(Level.INFO, "HackMap.remove key=" + key); //NOI18N
            
            Object ret = (delegate != null) ? delegate.remove(key) : null;
            
            if (debug) {
                LOG.log(Level.INFO, "removing kitInstance=" + ret //NOI18N
                    + " for mimeType=" + key); // NOI18N
            }
            
            return ret;
            } // synchronized (Settings.class)
        }
        
        Hashtable getOriginal() {
            return delegate;
        }

        private boolean shouldUseNbKit(String kitClass, String mimeType) {
            if (mimeType.startsWith("text/html") || //NOI18N
                mimeType.startsWith("text/rtf") || //NOI18N
                mimeType.startsWith("application/rtf")) //NOI18N
            {
                return false;
            } else {
                return kitClass.startsWith("javax.swing."); //NOI18N
            }
        }

        // Don't use CloneableEditorSupport.getEditorKit so that it can safely
        // fallback to JEP.createEKForCT if it doesn't find Netbeans kit.
        private EditorKit findKit(String mimeType) {
            Lookup lookup = MimeLookup.getLookup(MimePath.parse(mimeType));
            EditorKit kit = (EditorKit) lookup.lookup(EditorKit.class);
            return kit == null ? null : (EditorKit) kit.clone();
        }
    }
    
    private static final class DebugHashtable extends Hashtable {
        
        DebugHashtable(Hashtable h) {
            if (h != null) {
                putAll(h);
                LOG.log(Level.INFO, "Existing kit classNames mappings: " + this); //NOI18N
            }
        }
        
        public @Override Object put(Object key, Object value) {
            Object ret = super.put(key, value);
            LOG.log(Level.INFO, "registering mimeType=" + key //NOI18N
                + " -> kitClassName=" + value // NOI18N
                + " original was " + ret); // NOI18N
            return ret;
        }
        
        public @Override Object remove(Object key) {
            Object ret = super.remove(key);
            LOG.log(Level.INFO, "removing kitClassName=" + ret //NOI18N
                + " for mimeType=" + key); // NOI18N
            return ret;
        }
        
    }

    private void initAndCheckEditorKitTypeRegistry(String mimeType, String expectedKitClass) {
        String kitClass = JEditorPane.getEditorKitClassNameForContentType(mimeType);
        if (kitClass == null) {
            LOG.log(Level.WARNING, "Can't find JDK editor kit class for " + mimeType); //NOI18N
        } else if (expectedKitClass != null && !expectedKitClass.equals(kitClass)) {
            LOG.log(Level.WARNING, "Wrong JDK editor kit class for " + mimeType + //NOI18N
                ". Expecting: " + expectedKitClass + //NOI18N
                ", but was: " + kitClass); //NOI18N
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.