001    /*
002     @license.text@ 
003     */
004    package biz.hammurapi.codegen;
005    
006    import java.lang.reflect.Method;
007    import java.util.ArrayList;
008    import java.util.Collection;
009    import java.util.HashMap;
010    import java.util.HashSet;
011    import java.util.Iterator;
012    import java.util.List;
013    import java.util.Map;
014    import java.util.Set;
015    import java.util.StringTokenizer;
016    
017    import org.apache.bcel.classfile.JavaClass;
018    import org.w3c.dom.Element;
019    
020    import biz.hammurapi.util.Visitable;
021    import biz.hammurapi.util.Visitor;
022    import biz.hammurapi.xml.dom.DomSerializable;
023    
024    
025    /**
026     * Helper class to build net of interfaces 
027     * @author Pavel Vlasov
028     * @version $Revision: 1.8 $
029     */
030    public class InterfacePool {
031            List descriptors=new ArrayList();
032            Map nameMap=new HashMap();
033            
034            class WordEntry implements Visitable {
035                    String name;
036                    InterfaceDescriptor descriptor;
037                    // word -> WordEntry
038                    Map subWords=new HashMap();
039                    
040                    /**
041                     * Node with own descriptor is always 0 cardinality
042                     * @return
043                     */
044                    int getCardinality() {
045                            return descriptor==null ? 0 : subWords.size();
046                    }
047                    
048                    Set getCommonMethods() {
049                            final Set[] ret={null};
050                            accept(new Visitor() {
051                                    public boolean visit(Object target) {
052                                            WordEntry we=(WordEntry) target;
053                                            if (we.descriptor!=null) {
054                                                    if (ret[0]==null) {
055                                                            ret[0]=new HashSet(we.descriptor.methods);
056                                                    } else {
057                                                            ret[0].retainAll(we.descriptor.methods);
058                                                    }
059                                            }
060                                            return true;
061                                    }                               
062                            });
063                            
064                            return ret[0];
065                    }
066                    
067                    /**
068                     * Number of master interfaces in subinterfaces.
069                     * @return
070                     */
071                    int getMasterCount() {
072                            final int[] ret={0};
073                            accept(new Visitor() {
074                                    public boolean visit(Object target) {
075                                            WordEntry we=(WordEntry) target;
076                                            if (we.descriptor!=null && we.descriptor.isMaster()) {
077                                                    ++ret[0];
078                                            }
079                                            return true;
080                                    }                               
081                            });
082                            
083                            return ret[0];
084                    }
085    
086                    public boolean accept(Visitor visitor) {
087                            if (visitor.visit(this)) {
088                                    Iterator it=subWords.values().iterator();
089                                    while (it.hasNext()) {
090                                            ((Visitable) it.next()).accept(visitor);
091                                    }
092                                    return true;
093                            }
094                            return false;
095                    }
096            }
097            
098            WordEntry wordEntry=new WordEntry();
099            
100            {
101                    wordEntry.name="";
102            }
103            
104            public void discoverCommonDenominators() {
105                    wordEntry.accept(new Visitor() {
106                            public boolean visit(Object target) {
107                                    WordEntry we=(WordEntry) target;
108                                    if (we.descriptor==null && we.name!=null && we.name.length()>0 && we.getMasterCount()>1) {
109                                            Set methods=we.getCommonMethods();
110                                            if (!methods.isEmpty()) {
111                                                    InterfaceDescriptor d=new InterfaceDescriptor();
112                                                    d.name=we.name;
113                                                    nameMap.put(d.name, d);
114                                                    descriptors.add(d);
115                                                    d.methods.addAll(methods);
116                                                    d.isCommonDenominator=true;
117                                            }
118                                    }
119                                    return true;
120                            }                       
121                    });
122            }
123            
124            public void generateCommonDenominators(String packageName, Consumer consumer) throws GenerationException {
125                    Iterator it=descriptors.iterator();
126                    while (it.hasNext()) {
127                            InterfaceDescriptor d = (InterfaceDescriptor) it.next();
128                            if (d.isCommonDenominator && d.isMaster()) {
129                                    StringBuffer definition=new StringBuffer("public interface ");
130                                    if (packageName!=null && packageName.length()>0) {
131                                            definition.append(packageName);
132                                            definition.append(".");
133                                    }
134                                    definition.append(d.name);
135                                    Iterator sit=d.getSuperInterfaces(packageName).iterator();
136                                    if (sit.hasNext()) {
137                                            definition.append(" extends ");
138                                    }
139                                    while (sit.hasNext()) {
140                                            definition.append(sit.next());
141                                            if (sit.hasNext()) {
142                                                    definition.append(", ");
143                                            }
144                                    }
145                                    
146                                    Interface dif=new Interface(definition.toString(), "Common denominator interface", consumer.getListener());
147                                    Iterator mit=d.getOwnMethods().iterator();
148                                    while (mit.hasNext()) {
149                                            StringBuffer md=new StringBuffer();
150                                            String signature=(String) mit.next();
151                                            int idx=signature.indexOf('(');
152                                            int edx=signature.indexOf(")");
153                                            md.append(signature.substring(0, idx+1));
154                                            StringTokenizer st=new StringTokenizer(signature.substring(idx+1, edx), ",");
155                                            for (int i=1; st.hasMoreTokens(); i++) {
156                                                    md.append(st.nextToken());
157                                                    md.append(" p");
158                                                    md.append(i);
159                                                    if (st.hasMoreTokens()) {
160                                                            md.append(", ");
161                                                    }
162                                            }
163                                            md.append(signature.substring(edx));
164                                            dif.addMethod(md.toString(), null, "Common method", null);
165                                    }
166                                    consumer.consume(dif.getJavaClass());
167                            }
168                    }
169            }
170                            
171            public InterfaceDescriptor addInterface(final String name, Map attributes) {
172                String newName=name;
173                    for (int i=0; nameMap.containsKey(newName); i++) {
174                            newName=name+"_"+i;
175                    }
176                    
177                    InterfaceDescriptor d=new InterfaceDescriptor();
178                    d.name=newName;
179                    nameMap.put(d.name, d);
180                    descriptors.add(d);
181                    d.attributes=attributes;
182                    
183                    Collection words=new ArrayList();
184                    for (int i=0, j=0, l=newName.length(); i<=l; i++) {
185                            if (i==l && j<i) {
186                                    words.add(newName.substring(j, i));                             
187                            } else if (Character.isUpperCase(newName.charAt(i)) && j<i) {
188                                    words.add(newName.substring(j, i));
189                                    j=i;
190                            }
191                    }
192                    
193                    WordEntry currentWordEntry=wordEntry;
194                    Iterator it=words.iterator();
195                    while (it.hasNext()) {
196                            String word=(String) it.next();
197                            WordEntry nextWordEntry=(WordEntry) currentWordEntry.subWords.get(word);
198                            if (nextWordEntry==null) {
199                                    nextWordEntry=new WordEntry();
200                                    nextWordEntry.name=currentWordEntry.name+word;
201                                    currentWordEntry.subWords.put(word, nextWordEntry);
202                            }
203                            currentWordEntry=nextWordEntry;
204                    }
205                    currentWordEntry.descriptor=d;
206                    
207                    return d;
208            }
209            
210            public InterfaceDescriptor getDescriptor(String name) {
211                    return (InterfaceDescriptor) nameMap.get(name);     
212            }
213                    
214            public void addInterface(java.lang.Class theInterface) {
215                    InterfaceDescriptor id=new InterfaceDescriptor();
216                    id.isExternal=true;
217                    id.name=theInterface.getName();
218                    id.clazz=theInterface;
219                    for (int i=0, mc=theInterface.getMethods().length; i<mc; i++) {
220                            Method method = theInterface.getMethods()[i];
221                            StringBuffer sb=new StringBuffer(method.getReturnType().getName());
222                            sb.append(" ");
223                            sb.append(method.getName());
224                            sb.append("(");
225                            for (int j=0, pc=method.getParameterTypes().length; j<pc; j++) {
226                                    sb.append(method.getParameterTypes()[j].getName());
227                                    if (j<pc-1) {
228                                            sb.append(",");
229                                    }
230                            }
231                            sb.append(")");
232                            id.methods.add(sb.toString());
233                    }
234                    nameMap.put(id.name, id);
235                    descriptors.add(id);            
236            }
237                    
238            public class InterfaceDescriptor {
239                
240                    /**
241                     * Removes methods implemented by superinterfaces and returns remainder
242                     * @param name
243                     * @return
244                     */
245                    public Set getOwnMethods() {
246                            Set ret=new HashSet(methods);
247                            Iterator sit=getSuperInterfaces().iterator();
248                            while (sit.hasNext()) {
249                                    ret.removeAll(((InterfaceDescriptor) sit.next()).methods);
250                            }
251                            return ret;
252                    }
253                    
254                    public void addMethod(String signature) {
255                            methods.add(signature);
256                    }               
257                
258                    /**
259                     * 
260                     * @param name
261                     * @return Attributes from all interfaces for which this interface is master. 
262                     */
263                    public Map getAttributes() {
264                        Map ret=new HashMap();
265                        Iterator it=descriptors.iterator();
266                        while (it.hasNext()) {
267                            InterfaceDescriptor id=(InterfaceDescriptor) it.next();
268                            if (id.attributes!=null && isMaster()) {
269                                ret.putAll(id.attributes);
270                            }
271                        }
272                        return ret;
273                    }
274                    
275            /**
276             * @return Returns the clazz.
277             */
278            public java.lang.Class getInterfaceClass() {
279                return clazz;
280            }
281            /**
282             * @return Returns the isExternal.
283             */
284            public boolean isExternal() {
285                return isExternal;
286            }
287            /**
288             * @return Returns the name.
289             */
290            public String getName() {
291                return name;
292            }
293                    private java.lang.Class clazz;
294            String name;
295                    boolean isExternal;
296                    boolean isCommonDenominator;
297                    int counter;
298                    Map attributes;
299                    
300                    /**
301                     * Contains method signatures
302                     */
303                    Set methods=new HashSet();
304                    
305                    /**
306                     * 
307                     * @return Master interface descriptor
308                     */
309                    public InterfaceDescriptor getMaster() {
310                            Iterator it=descriptors.iterator();
311                            while (it.hasNext()) {
312                                    InterfaceDescriptor id=(InterfaceDescriptor) it.next();
313                                    if (id.methods.equals(methods)) {
314                                            return id;
315                                    }
316                            }
317                            return this;
318                    }
319                    
320                    public boolean isMaster() {
321                            return this==getMaster();
322                    }
323    
324                    Collection getSuperInterfaces() {
325                            Collection ret=new ArrayList();
326                            Iterator it=descriptors.iterator();
327                            Z:
328                            while (it.hasNext()) {
329                                    InterfaceDescriptor id=(InterfaceDescriptor) it.next();                         
330                                    if (!methods.equals(id.methods) && methods.containsAll(id.methods)) {
331                                            Iterator rit=ret.iterator();
332                                            while (rit.hasNext()) {
333                                                    InterfaceDescriptor rid=(InterfaceDescriptor) rit.next();
334                                                    if (rid.methods.containsAll(id.methods)) {
335                                                            continue Z;
336                                                    } else if (id.methods.containsAll(rid.methods)) {
337                                                            rit.remove();
338                                                    }
339                                            }
340                                            
341                                            ret.add(id);
342                                    }
343                            }
344                            return ret;
345                    }       
346                    
347                    public Collection getSuperInterfaces(String packageName) {
348                            Collection ret=new ArrayList();
349                            Iterator it=((InterfaceDescriptor) nameMap.get(name)).getSuperInterfaces().iterator();
350                            while (it.hasNext()) {
351                                    InterfaceDescriptor interfaceDescriptor = (InterfaceDescriptor) it.next();
352                                    String sname=interfaceDescriptor.getMaster().getName();
353                                    if (!interfaceDescriptor.isExternal) {
354                                            sname=((packageName==null || packageName.length()==0) ? "" : packageName + ".")+sname;
355                                    }
356                                    
357                                    if (!ret.contains(sname)) {
358                                            ret.add(sname);
359                                    }
360                            }
361                            return ret;
362                    }                       
363            }
364            
365            public static void main(String[] args) throws GenerationException {
366                    InterfacePool pool=new InterfacePool();
367                    pool.addInterface(DomSerializable.class);
368                    
369                    InterfaceDescriptor a = pool.addInterface("PersonA", null);
370                    a.addMethod("java.lang.String getName() throws java.sql.SQLException");
371                    a.addMethod("int getA()");
372                    a.addMethod("void toDom("+Element.class.getName()+")");
373                    
374                    InterfaceDescriptor b = pool.addInterface("PersonB", null);
375                    b.addMethod("java.lang.String getName() throws java.sql.SQLException");
376                    b.addMethod("int getB()");
377                    b.addMethod("void toDom("+Element.class.getName()+")");
378                    
379                    pool.discoverCommonDenominators();
380    
381                    pool.generateCommonDenominators("test", new Consumer() {
382    
383                            public void consume(JavaClass javaClass) {
384                                    System.out.println(javaClass.toString());
385                            }
386    
387                            public GenerationListener getListener() {
388                                    return null;
389                            }
390                    });
391            }
392    }