ClassInfoStorage.java :  » IDE-Netbeans » editor » org » netbeans » modules » editor » mimelookup » impl » Java Open Source

Java Open Source » IDE Netbeans » editor 
editor » org » netbeans » modules » editor » mimelookup » impl » ClassInfoStorage.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.mimelookup.impl;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.netbeans.spi.editor.mimelookup.Class2LayerFolder;
import org.netbeans.spi.editor.mimelookup.InstanceProvider;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;

/**
 *
 * @author vita
 */
public final class ClassInfoStorage {
    
    public static final String PROP_CLASS_INFO_CHANGED = "Class2Paths.PROP_CLASS_MAPPING_CHANGED"; //NOI18N
    public static final String PROP_CLASS_INFO_ADDED = "Class2Paths.PROP_CLASS_MAPPING_ADDED"; //NOI18N
    public static final String PROP_CLASS_INFO_REMOVED = "Class2Paths.PROP_CLASS_MAPPING_REMOVED"; //NOI18N
    
    private static Logger LOG = Logger.getLogger(ClassInfoStorage.class.getName());
    
    private static ClassInfoStorage instance = null;
    
    public static synchronized ClassInfoStorage getInstance() {
        if (instance == null) {
            instance = new ClassInfoStorage();
        }
        return instance;
    }
    
    private Lookup.Result<Class2LayerFolder> mappers = null;
    private L mappersListener = null;

    private final String LOCK = new String("Class2Paths.LOCK"); //NOI18N
    private Map<String,Info> mapping = new HashMap<String,Info>();
    
    private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    
    /** Creates a new instance of Class2Paths */
    private ClassInfoStorage() {
        mappers = Lookup.getDefault().lookupResult(Class2LayerFolder.class);
        
        mappersListener = new L();
        mappers.addLookupListener(WeakListeners.create(LookupListener.class, mappersListener, mappers));
        
        rebuild();
    }

    public Info getInfo(String className) {
        synchronized (LOCK) {
            if (mapping.containsKey(className)) {
                return mapping.get(className);
            } else {
                return new Info(this, className, null, null);
            }
        }
    }
    
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }
    
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }

    private List<PropertyChangeEvent> rebuild() {
        synchronized (LOCK) {
            // Gather the new mapping information
            Map<String,Info> newMapping = new HashMap<String,Info>();
            for (Class2LayerFolder mapper : mappers.allInstances()) {
                String className = mapper.getClazz().getName();
                String path = mapper.getLayerFolderName();
                InstanceProvider ip = mapper.getInstanceProvider();

                if (path != null) {
                    path = path.trim();
                }
                
                if ((path == null || path.length() == 0) && ip == null) {
                    // Hmm, why anybody registered mapper, which doesn't provide any info.
                    continue;
                }
                
                if (!newMapping.containsKey(className)) {
                    newMapping.put(className, new Info(this, className, path, ip));
                } else {
                    LOG.warning("The mapping for class '" + className + "' to folder '" +  //NOI18N
                        path + "' and InstanceProvider '" + ip + "' has already been " + //NOI18N
                        "defined by another mapper. Ignoring mapper " + mapper); //NOI18N
                }
            }
        
            // Compute differences
            Set<String> removed = new HashSet<String>(mapping.keySet());
            removed.removeAll(newMapping.keySet());
            
            Set<String> added = new HashSet<String>(newMapping.keySet());
            added.removeAll(mapping.keySet());
            
            Set<String> changed = new HashSet<String>();
            for (String className : newMapping.keySet()) {
                if (mapping.containsKey(className) && 
                    !Utilities.compareObjects(newMapping.get(className), mapping.get(className)))
                {
                    changed.add(className);
                }
            }
            
            // Update the mapping
            mapping.clear();
            mapping.putAll(newMapping);
            
            // Generate events
            List<PropertyChangeEvent> events = new ArrayList<PropertyChangeEvent>(3);
            if (!removed.isEmpty()) {
                events.add(new PropertyChangeEvent(this, PROP_CLASS_INFO_REMOVED, null, removed));
            }
            if (!added.isEmpty()) {
                events.add(new PropertyChangeEvent(this, PROP_CLASS_INFO_ADDED, null, added));
            }
            if (!changed.isEmpty()) {
                events.add(new PropertyChangeEvent(this, PROP_CLASS_INFO_CHANGED, null, changed));
            }
            
            return events;
        }
    }
    
    private class L implements LookupListener {

        public void resultChanged(LookupEvent ev) {
            // Update mapping information & fire change events if neccessary
            for (PropertyChangeEvent event : rebuild()) {
                pcs.firePropertyChange(event);
            }
        }
        
    } // End of L class
    
    public static final class Info {
        private ClassInfoStorage storage;
        private String className;
        private String extraPath;
        private String instanceProviderClass;
        private Reference<InstanceProvider<?>> ref; // TODO: This should really be a timed-weak-ref
        
        private Info(ClassInfoStorage storage, String className, String extraPath, InstanceProvider<?> instanceProvider) {
            this.storage = storage;
            this.className = className;
            this.extraPath = extraPath == null ? "" : extraPath; //NOI18N
            if (instanceProvider != null) {
                this.instanceProviderClass = instanceProvider.getClass().getName();
                this.ref = new WeakReference<InstanceProvider<?>>(instanceProvider);
            }
        }
        
        public String getClassName() {
            return className;
        }
        
        public String getExtraPath() {
            return extraPath;
        }
        
        public String getInstanceProviderClass() {
            return instanceProviderClass;
        }
        
        public InstanceProvider<?> getInstanceProvider() {
            synchronized (storage.LOCK) {
                // There was no instance provider specified
                if (ref == null) {
                    return null;
                }
                
                InstanceProvider ip = (InstanceProvider) ref.get();
                if (ip == null) {
                    // Instance provider has been GCed, recreate it
                    for (Class2LayerFolder mapper : storage.mappers.allInstances()) {
                        String className = mapper.getClazz().getName();
                        
                        if (this.className.equals(className)) {
                            ip = mapper.getInstanceProvider();
                            break;
                        }
                    }
                    
                    if (ip != null) {
                        ref = new WeakReference<InstanceProvider<?>>(ip);
                    }
                }
                
                return ip;
            }
        }

        public @Override boolean equals(Object obj) {
            if (obj instanceof Info) {
                Info info = (Info) obj;
                return  this.className.equals(info.className) &&
                        Utilities.compareObjects(this.extraPath, info.extraPath) && 
                        Utilities.compareObjects(this.instanceProviderClass, info.instanceProviderClass);
            } else {
                return false;
            }
        }

        public @Override int hashCode() {
            int hashCode = className.hashCode();
            
            if (extraPath != null) {
                hashCode += 7 * extraPath.hashCode();
            }
            if (instanceProviderClass != null) {
                hashCode += 13 * instanceProviderClass.hashCode();
            }
            
            return hashCode;
        }

        public @Override String toString() {
            return "ClassInfoStorage.Info[className=" + className + ",extraPath=" + extraPath + ",instanceProviderClass=" + instanceProviderClass + "]"; // NOI18N
        }

    } // End of Info class
}
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.