|
What this is
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; import java.util.*; import org.openide.src.*; import java.lang.reflect.Modifier; import java.text.MessageFormat; import org.netbeans.jmi.javamodel.Method; import org.netbeans.modules.javacore.internalapi.JMIElementCookie; import org.openide.ErrorManager; import org.openide.loaders.DataObject; import org.openide.filesystems.FileObject; /** This class support special type of Java connections - * between interfaces and implementation * @author Petr Hamernik */ class InterfaceConnection extends Object { private static final boolean DEBUG = false; static boolean synchronizeInterfaces(JavaConnections.Event evt, LinkedList changeProcessors, SourceElement source) { JavaConnections.Change[] changes = evt.getChanges(); TreeMap cacheMap = new TreeMap(); int origCPSize = changeProcessors.size(); Map changedMethodMap = new HashMap(); for (int i = 0; i < changes.length; i++) { try { switch (changes[i].getChangeType()) { case JavaConnections.TYPE_METHODS_ADD: Element[] elements = changes[i].getElements(); for (int j = 0; j < elements.length; j++) { MethodElement m = (MethodElement) elements[j]; ClassElement interf = m.getDeclaringClass(); Vector classes = findClassImplementing(interf, source, cacheMap); if (!classes.isEmpty()) { m = createEmptyMethodFrom(m); MethodElement.Key key = new MethodElement.Key(m); if (changedMethodMap.containsKey(key)) { if (compareMethods((MethodElement)changedMethodMap.get(key), m)) { continue; } } Enumeration classesEn=classes.elements(); while(classesEn.hasMoreElements()) { ClassElement c = (ClassElement)classesEn.nextElement(); MethodElement updatedMethod = findMatchingMethod(c, m); if (updatedMethod == null) { changeProcessors.add(new AddMethodProcessor(interf, c, m)); } else if (!compareMethods(updatedMethod, m)) { changeProcessors.add(new ChangeMethodProcessor(interf, c, updatedMethod, m)); } changedMethodMap.put(key, m); } } } break; case JavaConnections.TYPE_METHODS_CHANGE: MethodElement oldMethod = (MethodElement) changes[i].getOldElement(); MethodElement newMethod = (MethodElement) changes[i].getNewElement(); ClassElement interf = newMethod.getDeclaringClass(); Vector classes = findClassImplementing(interf, source, cacheMap); if (!classes.isEmpty()) { MethodElement addingMethod = null; Enumeration classesEn=classes.elements(); while(classesEn.hasMoreElements()) { ClassElement c = (ClassElement)classesEn.nextElement(); MethodElement updatedMethod = findMatchingMethod(c, oldMethod); if (updatedMethod == null) { // next try: there can be method matching the new signature. //System.out.println("Can't find old method: " + oldMethod.toString()); // NOI18N updatedMethod = findMatchingMethod(c, newMethod); if (updatedMethod == null) { // there's nothing that matches old or new signature. if (addingMethod == null) { addingMethod = createEmptyMethodFrom(newMethod); } MethodElement.Key key = new MethodElement.Key(addingMethod); if (changedMethodMap.containsKey(key)) { if (compareMethods((MethodElement)changedMethodMap.get(key), addingMethod)) { continue; } } changeProcessors.add(new AddMethodProcessor(interf, c, addingMethod)); changedMethodMap.put(key, addingMethod); continue; } } if (compareMethods(updatedMethod, newMethod)) { continue; } if (updatedMethod != null) { MethodElement.Key key = new MethodElement.Key(newMethod); if (changedMethodMap.containsKey(key)) { if (compareMethods((MethodElement)changedMethodMap.get(key), newMethod)) { continue; } } changeProcessors.add(new ChangeMethodProcessor(interf, c, updatedMethod, newMethod)); changedMethodMap.put(key, newMethod); } } } break; } } catch (SourceException e) { ErrorManager.getDefault().notify(e); } } return origCPSize!=changeProcessors.size(); } private static MethodElement createEmptyMethodFrom(MethodElement m) throws SourceException { MethodElement newMet=(MethodElement)m.clone(); JMIElementCookie cookie = (JMIElementCookie)m.getCookie(JMIElementCookie.class); // [PENDING] if (cookie != null) { Method method = (Method) cookie.getElement(); if (method != null) { newMet.setBody(JavaConnections.SETTINGS.getGenerateReturnAsString(method.getType())); } } newMet.setModifiers(Modifier.PUBLIC); return newMet; } private static MethodElement findMatchingMethod(ClassElement clazz, MethodElement elem) { MethodParameter[] params = elem.getParameters(); Type[] paramTypes = new Type[params.length]; for (int i = 0; i < paramTypes.length; i++) { paramTypes[i] = params[i].getType(); } return clazz.getMethod(elem.getName(), paramTypes); } private static Vector findClassImplementing(ClassElement interf, SourceElement source, Map cache) { Identifier id = interf.getName(); String key = id.getFullName(); // System.out.println("find("+key+")"); // NOI18N Vector ret = (Vector)cache.get(key); // System.out.println(" get =>"+(ret == null ? " null" : ""+ret.length)); // NOI18N if (ret == null) { Vector list = new Vector(); ClassElement[] classes = source.getAllClasses(); for (int i = 0; i < classes.length; i++) { if (classes[i].isInterface()) continue; Identifier[] interfaces = classes[i].getInterfaces(); for (int j = 0; j < interfaces.length; j++) { if (interfaces[j].compareTo(id, false)) { // System.out.println(" found:"+classes[i]); // NOI18N list.add(classes[i]); break; } } } cache.put(key, list); ret=list; } return ret; } static void interfacesAdded(Identifier[] interfaces, ClassElement cl, JavaDataObject jdo) { if (!JavaConnections.SETTINGS.isEnabled()) // disabled return; if (jdo.getSynchronizationType() == JavaDataObject.CONNECT_NOT) return; LinkedList methodList = new LinkedList(); addAllMethods(interfaces, methodList, jdo.getPrimaryFile()); Map changedMethods = new HashMap(); LinkedList changesProcessors = new LinkedList(); Iterator methodsEn=methodList.iterator(); while(methodsEn.hasNext()) { MethodElement m=(MethodElement)methodsEn.next(); ClassElement interf = m.getDeclaringClass(); try { m = createEmptyMethodFrom(m); } catch (SourceException e) { ErrorManager.getDefault().notify(e); return; } MethodElement updatedMethod = findMatchingMethod(cl, m); MethodElement.Key key = new MethodElement.Key(m); if (changedMethods.containsKey(key) && compareMethods((MethodElement)changedMethods.get(key), m)) { continue; } if (updatedMethod == null) { changesProcessors.add(new AddMethodProcessor(interf, cl, m)); } else { if (compareMethods(updatedMethod, m)) { continue; } changesProcessors.add(new ChangeMethodProcessor(interf, cl, updatedMethod, m)); } changedMethods.put(key, m); } if (!changesProcessors.isEmpty()) jdo.startChangeProcessors(changesProcessors); } private static void addAllMethods(Identifier[] interfaces, LinkedList allMethods, FileObject projectArtefact) { for (int i = 0; i < interfaces.length; i++) { ClassElement interf = ClassElement.forName(interfaces[i].getFullName(), projectArtefact); if ((interf != null) && (interf.isInterface())) { MethodElement[] methods = interf.getMethods(); for (int j = 0; j < methods.length; j++) { allMethods.add(methods[j]); } Identifier[] recurseInterfaces = interf.getInterfaces(); if (recurseInterfaces.length > 0) addAllMethods(recurseInterfaces, allMethods, projectArtefact); } } } static boolean sourceCheck(LinkedList changeProcessors, SourceElement source) { ClassElement[] classes = source.getAllClasses(); int i; boolean changed = false; if (DEBUG) System.out.println("Checking source..."); // NOI18N try { FileObject sourceFile = ((DataObject)source.getCookie(DataObject.class)).getPrimaryFile(); for (i = 0; i < classes.length; i++) { ClassElement cls = classes[i]; if (!cls.isClassOrInterface()) { // do not sync interfaces. continue; } ClassElement superClass = null; Set allInterfaces = new HashSet(13); Set implInterfaces = new HashSet(13); Identifier superid = cls.getSuperclass(); if (DEBUG) System.out.println("Found class " + cls.getName()); // NOI18N collectInterfaces(cls, allInterfaces, implInterfaces, true, false,sourceFile); if (superid != null) { // if we have not any superclass, it makes no sense to exclude interfaces implemented // by superclasses. superClass = ClassElement.forName(superid.getFullName(), sourceFile); allInterfaces.removeAll(implInterfaces); } // Get all methods from all implemented interfaces, throwing out // methods with the same declarations. TreeMap implement = new TreeMap(new DefaultMethodComparator()); Iterator itfIter = allInterfaces.iterator(); if (DEBUG) System.out.println("Number of interfaces: " + allInterfaces.size()); // NOI18N while (itfIter.hasNext()) { ClassElement itf = (ClassElement)itfIter.next(); MethodElement methods[] = itf.getMethods(); for (int j = 0; j < methods.length; j++) { MethodElement m = methods[j]; if (!implement.containsKey(m)) { //if (DEBUG) System.out.println("Recording method " + m.toString() + " in itf " + itf.getName().getFullName()); // NOI18N implement.put(m, itf); } } } // if the class has an abstract superclass, go up through inheritance // chain and add all abstract methods from abstract superclasses. for (ClassElement s = superClass; s != null && (s.getModifiers() & Modifier.ABSTRACT) > 0; s = (s.getSuperclass() == null) ? null : ClassElement.forName(s.getSuperclass().getFullName(), sourceFile)) { MethodElement[] mets = s.getMethods(); for (int j = 0; j < mets.length; j++) { MethodElement m = mets[j]; if ((m.getModifiers() & Modifier.ABSTRACT) > 0) { if (!implement.containsKey(m)) { implement.put(m, s); } } } } // Now, exclude all methods from already implemented interfaces (== interfaces implemented // by nonabstract superclasses) itfIter = implInterfaces.iterator(); while (itfIter.hasNext()) { ClassElement itf = (ClassElement)itfIter.next(); MethodElement methods[] = itf.getMethods(); if (DEBUG) System.out.println("Checking implemented itf " + itf.getName().getFullName() + " for methods..."); // NOI18N for (int j = 0; j < methods.length; j++) { MethodElement m = methods[j]; if (DEBUG) { if (implement.remove(m) != null) { System.out.println("Excluding method " + m.toString() + " - is implemented in " + // NOI18N itf.getName().getFullName()); } } implement.remove(m); } } // implement now contains all unique methods that are to be implemented // by the class. We have to filter out methods, that have been implemented // by one of the superclasses. Iterator mIter = implement.keySet().iterator(); List unimplemented = new LinkedList(); while (mIter.hasNext()) { MethodElement m = (MethodElement)mIter.next(); MethodParameter[] params = m.getParameters(); Type[] paramTypes = new Type[params.length]; ClassElement implementingSuper; for (int j = 0; j < params.length; j++) { paramTypes[j] = params[j].getType(); } implementingSuper = superClass == null ? null : findMethodInSuperclasses(superClass, m.getName(), paramTypes, sourceFile); if (implementingSuper != null) { if (DEBUG) System.out.println("Method " + m.toString() + "is implemented in superclass " + implementingSuper.getName().getFullName()); // NOI18N } else { if (DEBUG) System.out.println("Method " + m.toString() + "is not implemented.\n\tattaching to interface " + ((ClassElement)implement.get(m)).getName().getFullName()); // NOI18N unimplemented.add(m); } } if (!unimplemented.isEmpty()) { changed |= implementMethods(cls, unimplemented, changeProcessors); } } if (DEBUG) System.out.println("Source check done."); // NOI18N } catch (SourceException e) { ErrorManager.getDefault().notify(e); } return changed; } /** Comparator that orders methods as follows: * 1. sorts them in ascending alphabetical order * 2. methods with the same name, but less arguments are preceding methods with more arguments * 3. methods with the same number of arguments are sorted by their argument declarations */ private static class DefaultMethodComparator implements Comparator { public int compare(Object a,Object b) { MethodElement ma = (MethodElement)a; MethodElement mb = (MethodElement)b; int result; if (ma == mb) return 0; result = ma.getName().getSourceName().compareTo(mb.getName().getSourceName()); if (result != 0) return result; result = ma.getParameters().length - mb.getParameters().length; if (result != 0) return result; // compare parameter names/types. MethodParameter[] paramA = ma.getParameters(); MethodParameter[] paramB = mb.getParameters(); for (int i = 0; i < paramA.length; i++) { result = paramA[i].getFullString().compareTo(paramB[i].getFullString()); if (result != 0) return result; } return 0; } public boolean equals(Object ob) { return ob instanceof DefaultMethodComparator; } } private static boolean implementMethods(ClassElement cls, List unimplemented, LinkedList changeProcessors) throws SourceException { boolean changed = false; MethodElement[] oldMethods = cls.getMethods(); MethodElement[] newMethods = (MethodElement[])unimplemented.toArray(new MethodElement[0]); int i; Iterator it; int results[]={}; // = col.pairElements(oldMethods, newMethods, col.getComparators()); for (i = 0; i < results.length; i++) { MethodElement newm = newMethods[i]; ClassElement itf = newm.getDeclaringClass(); if (results[i] == -1) { newm = createEmptyMethodFrom(newm); // entirely new/unrecognized method //System.out.println("ClassElement: " + itf.getName().getFullName()); // NOI18N if (DEBUG) System.out.println("Unimplemented (new) method: " + newm.toString()); // NOI18N changeProcessors.add(new AddMethodProcessor(itf, cls, newm)); changed = true; } else { // we need to include only methods that have somehow changed in the change // processor list. MethodElement oldm = oldMethods[results[i]]; // if (!oldm.compareTo(newm)) { if (!compareMethods(oldm,newm)) { if (DEBUG) System.out.println("Altered method: " + oldm.toString() + " -> " + newm.toString()); // NOI18N newm = createEmptyMethodFrom(newm); changeProcessors.add(new ChangeMethodProcessor(itf, cls, oldm, newm)); changed = true; } else { if (DEBUG) System.out.println("Method " + newm.toString() + " hasn't been changed."); // NOI18N } } } if (DEBUG) System.out.println("Class synchronization finished."); // NOI18N return changed; } /* This method was taken from ElementsCollection and temporary moved here due to ongoing Java module refactoring * also due to unresolved issue with Comparators */ static int[] pairElements(MemberElement[] oldArray, MemberElement[] newArray, Comparator[] comps) { int oldSize = oldArray.length; int newSize = newArray.length; if (newArray.length > 0 && !(newArray[0] instanceof MemberElement)) { throw new IllegalArgumentException("Got " + newArray[0].getClass()); // NOI18N } int[] result = new int[newSize]; for (int i = 0; i < newSize; i++) result[i] = -1; if ((oldSize == 0) || (newSize == 0)) return result; int i, j, k; int match = 0; BitSet used = new BitSet(oldArray.length); for (k = 0; k < comps.length; k++) { Comparator comp = comps[k]; for (i = 0, j = 0; (i < newSize) && (j < oldSize);) { if (used.get(j)) { j++; continue; } if (result[i] != -1) { i++; continue; } if (comp.compare(newArray[i], oldArray[j]) == 0) { result[i] = j; used.set(j); match++; } i++; j++; } for (i = newSize - 1, j = oldSize - 1; (i >= 0) && (j >= 0);) { if (used.get(j)) { j--; continue; } if (result[i] != -1) { i--; continue; } if (comp.compare(newArray[i], oldArray[j]) == 0) { result[i] = j; used.set(j); match++; } i--; j--; } for (i = 0; (i < newSize) && (match < newSize); i++) { if (result[i] != -1) continue; for (j = 0; j < oldSize; j++) { if (used.get(j)) continue; if (comp.compare(newArray[i], oldArray[j]) == 0) { result[i] = j; used.set(j); match++; } } } } return result; } /** Compares old implementation of method (a) with the new declaration suggested from the interface (b). Returns true if the method declarations completely match, so it is not necessary to update the source. Note: since interface methods are public abstract by definition, no check is made on method modifiers. */ private static boolean compareMethods(MethodElement a, MethodElement b) { if (!a.getName().compareTo(b.getName(), false)) { return false; } if (!a.getReturn().compareTo(b.getReturn(), false)) { return false; } MethodParameter[] paramA = a.getParameters(); MethodParameter[] paramB = b.getParameters(); if (paramA.length != paramB.length) { return false; } int i; for (i = 0; i < paramA.length; i++) { if (!paramA[i].compareTo(paramB[i], true, false)) { return false; } } Identifier[] excA = a.getExceptions(); Identifier[] excB = b.getExceptions(); if (excA.length != excB.length) { return false; } for (i = 0; i < excA.length; i++) { if (!excA[i].compareTo(excB[i], false)) { return false; } } return true; } private static ClassElement findMethodInSuperclasses(ClassElement cls, Identifier name, Type[] paramTypes, FileObject sourceFile) { MethodElement el = cls.getMethod(name, paramTypes); if (el != null) { // return cls iff the method is not abstract. Otherwise it means that the most // direct superclass mentioning this method declares it as abstract (and that // it is not implemented at all). return (el.getModifiers() & Modifier.ABSTRACT) > 0 ? null : cls; } Identifier superclass = cls.getSuperclass(); if (superclass == null) { return null; } cls = ClassElement.forName(superclass.getFullName(), sourceFile); if (cls != null) { return findMethodInSuperclasses(cls, name, paramTypes, sourceFile); } else { return null; } } /** Helper method that recursively walks inheritance chain and collects methods from abstract * superclasses or superinterfaces. The walk is depth-first (superclass before interfaces). * Interfaces found directly at `cls' or at abstract superclasses are included - recorded * into `include' set. |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
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.