001    /*
002     * Copyright (C) 2006-2007 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.codehaus.gmaven.runtime.support.stubgen.model;
018    
019    import org.codehaus.gmaven.runtime.support.stubgen.UnexpectedNodeException;
020    import org.codehaus.gmaven.runtime.support.stubgen.parser.Node;
021    import org.codehaus.gmaven.runtime.support.stubgen.parser.Parser;
022    import org.codehaus.gmaven.runtime.support.stubgen.parser.ParserFactory;
023    import org.codehaus.gmaven.runtime.support.stubgen.parser.SourceType;
024    
025    import java.io.BufferedReader;
026    import java.io.InputStreamReader;
027    import java.io.Reader;
028    import java.net.URL;
029    import java.util.Iterator;
030    import java.util.LinkedHashSet;
031    import java.util.Set;
032    
033    /**
034     * Provides support for {@link ModelFactory} implementations.
035     *
036     * @version $Id: ModelFactorySupport.java 18 2009-07-16 09:39:40Z user57 $
037     * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
038     */
039    public abstract class ModelFactorySupport
040        implements ModelFactory
041    {
042        protected final ParserFactory factory;
043    
044        protected Parser parser;
045    
046        protected Node lastNode;
047    
048        protected SourceDef source;
049    
050        protected ClassDef clazz;
051    
052        protected ModelFactorySupport(final ParserFactory factory) {
053            assert factory != null;
054    
055            this.factory = factory;
056        }
057    
058        public SourceDef create(final URL input) throws Exception {
059            assert input != null;
060    
061            return create(input, SourceType.forURL(input));
062        }
063    
064        public SourceDef create(final URL input, final SourceType type) throws Exception {
065            assert input != null;
066            assert type != null;
067    
068            // Setup the root model element
069            source = createRoot(input, type);
070    
071            // Reset internal state
072            lastNode = null;
073            clazz = null;
074    
075            // Create a new parser for the source type
076            parser = factory.create(source.getType());
077    
078            // Parse the source
079            Reader reader = new BufferedReader(new InputStreamReader(input.openStream()));
080            Node node;
081    
082            try {
083                node = parser.parse(reader, input.toExternalForm());
084            }
085            finally {
086                reader.close();
087            }
088    
089            // Process the tree
090            process(node);
091    
092            return source;
093        }
094    
095        protected SourceDef createRoot(final URL input, final SourceType type) {
096            assert input != null;
097            assert type != null;
098    
099            SourceDef def = new SourceDef();
100            def.setUrl(input);
101            def.setType(type);
102    
103            addDefaultImports(def);
104    
105            return def;
106        }
107    
108        protected abstract void addDefaultImports(final SourceDef model);
109    
110        //
111        // Processing
112        //
113    
114        protected void process(Node node) {
115            assert node != null;
116    
117            while (node != null) {
118                if (node.is("PACKAGE_DEF")) {
119                    packageDef(node);
120                }
121                else if (node.is(new String[] { "STATIC_IMPORT", "IMPORT" })) {
122                    importDef(node);
123                }
124                else if (node.is("CLASS_DEF")) {
125                    classDef(node);
126                }
127                else if (node.is("INTERFACE_DEF")) {
128                    interfaceDef(node);
129                }
130                else if (node.is("METHOD_DEF")) {
131                    methodDef(node);
132                }
133                else if (node.is("ENUM_DEF")) {
134                    enumDef(node);
135                }
136                else if (node.is("ANNOTATION_DEF")) {
137                    annotationDef(node);
138                }
139                else {
140                    // everything else should be some sort of statement
141                    source.addStatement(node);
142                }
143    
144                node = node.nextSibling();
145            }
146        }
147    
148        protected void packageDef(final Node parent) {
149            assert parent != null;
150    
151            PackageDef def = new PackageDef();
152    
153            Node node = parent.firstChild();
154    
155            node = node.skip("ANNOTATIONS");
156    
157            String name = qualifiedName(node);
158    
159            def.setName(name);
160    
161            source.setPackage(def);
162        }
163    
164        protected void importDef(final Node parent) {
165            assert parent != null;
166    
167            ImportDef def = new ImportDef();
168    
169            if (parent.is("STATIC_IMPORT")) {
170                // import is like "import static foo"
171                def.setStatic(true);
172            }
173    
174            Node node = parent.firstChild();
175    
176            if (node.is("LITERAL_as")) {
177                // import is like "import foo as bar"
178                node = node.firstChild();
179                Node aliasNode = node.nextSibling();
180                def.setAlias(identifier(aliasNode));
181            }
182    
183            if (node.isLeaf()) {
184                // import is like "import Foo"
185                def.setType(identifier(node));
186            }
187            else {
188                Node packageNode = node.firstChild();
189                String packageName = qualifiedName(packageNode);
190                def.setPackage(packageName);
191    
192                Node nameNode = packageNode.nextSibling();
193                if (nameNode.is("STAR")) {
194                    // import is like "import foo.*"
195                    def.setWildcard(true);
196                }
197                else {
198                    String name = identifier(nameNode);
199                    // import is like "import foo.Bar"
200                    def.setType(name);
201                }
202            }
203    
204            source.addImport(def);
205        }
206    
207        protected void interfaceDef(final Node parent) {
208            assert parent != null;
209    
210            InterfaceDef def = new InterfaceDef();
211    
212            clazz = def;
213    
214            Node node = parent.firstChild();
215    
216            node = modifiers(def, node);
217    
218            node = name(def, node);
219    
220            if (node.is("EXTENDS_CLAUSE")) {
221                def.getImplements().addAll(interfaces(node));
222                node = node.nextSibling();
223            }
224    
225            javadocs(def, parent);
226    
227            objectBlock(node);
228    
229            source.addClass(def);
230        }
231    
232        protected void classDef(final Node parent) {
233            assert parent != null;
234    
235            ClassDef def = new ClassDef();
236            def.addImplements("groovy.lang.GroovyObject");
237    
238            clazz = def;
239    
240            Node node = parent.firstChild();
241    
242            node = modifiers(def, node);
243    
244            node = name(def, node);
245    
246            if (node.is("TYPE_PARAMETERS")) {
247                //
248                // FIXME: Support generics
249                //
250                node = node.nextSibling();
251            }
252    
253            if (node.is("EXTENDS_CLAUSE")) {
254                def.setSuperClass(type(node));
255                node = node.nextSibling();
256            }
257    
258            if (node.is("IMPLEMENTS_CLAUSE")) {
259                def.getImplements().addAll(interfaces(node));
260                node = node.nextSibling();
261            }
262    
263            javadocs(def, parent);
264    
265            objectBlock(node);
266    
267            source.addClass(def);
268        }
269    
270        protected void enumDef(final Node parent) {
271            assert parent != null;
272    
273            parent.ensure("ENUM_DEF");
274    
275            EnumDef def = new EnumDef();
276    
277            clazz = def;
278    
279            Node node = parent.firstChild();
280    
281            node = modifiers(def, node);
282    
283            node = name(def, node);
284    
285            if (node.is("TYPE_PARAMETERS")) {
286                //
287                // FIXME: Support generics
288                //
289                node = node.nextSibling();
290            }
291    
292            if (node.is("EXTENDS_CLAUSE")) {
293                def.setSuperClass(type(node));
294                node = node.nextSibling();
295            }
296    
297            if (node.is("IMPLEMENTS_CLAUSE")) {
298                def.getImplements().addAll(interfaces(node));
299                node = node.nextSibling();
300            }
301    
302            javadocs(def, parent);
303    
304            objectBlock(node);
305    
306            source.addClass(def);
307        }
308    
309        protected void annotationDef(final Node parent) {
310            assert parent != null;
311    
312            parent.ensure("ANNOTATION_DEF");
313    
314            AnnotationDef def = new AnnotationDef();
315    
316            clazz = def;
317    
318            Node node = parent.firstChild();
319    
320            node = modifiers(def, node);
321    
322            node = name(def, node);
323    
324            if (node.is("TYPE_PARAMETERS")) {
325                //
326                // FIXME: Support generics
327                //
328                node = node.nextSibling();
329            }
330    
331            if (node.is("EXTENDS_CLAUSE")) {
332                def.getImplements().addAll(interfaces(node));
333                node = node.nextSibling();
334            }
335    
336            javadocs(def, parent);
337    
338            objectBlock(node);
339    
340            source.addClass(def);
341        }
342    
343        protected void objectBlock(final Node parent) {
344            assert parent != null;
345    
346            parent.ensure("OBJBLOCK");
347    
348            for (Node node = parent.firstChild(); node != null; node = node.nextSibling()) {
349                if (node.is("OBJBLOCK")) {
350                    objectBlock(node);
351                }
352                else if (node.is("METHOD_DEF")) {
353                    methodDef(node);
354                }
355                else if (node.is("ANNOTATION_FIELD_DEF")) {
356                    annotationFieldDef(node);
357                }
358                else if (node.is("CTOR_IDENT")) {
359                    constructorDef(node);
360                }
361                else if (node.is("VARIABLE_DEF")) {
362                    fieldDef(node);
363                }
364                else if (node.is("ENUM_DEF")) {
365                    enumDef(node);
366                }
367                else if (node.is("ENUM_CONSTANT_DEF")) {
368                    enumConstantDef(node);
369                }
370                else if (node.is(new String[] { "STATIC_INIT", "INSTANCE_INIT" })) {
371                    // Ignore
372                }
373                else {
374                    throw new UnexpectedNodeException(node);
375                }
376            }
377        }
378    
379        protected void constructorDef(final Node parent) {
380            assert parent != null;
381    
382            ConstructorDef def = new ConstructorDef();
383    
384            Node node = parent.firstChild();
385    
386            node = modifiers(def, node);
387    
388            node = parameters(def, node);
389    
390            node = throwz(def, node);
391    
392            superParameters(def, node);
393    
394            javadocs(def, parent);
395    
396            clazz.addConstructor(def);
397        }
398    
399        protected void superParameters(final ConstructorDef target, final Node parent) {
400            assert target != null;
401            assert parent != null;
402    
403            Node node = parent;
404    
405            if (node.is("SLIST")) {
406                node = node.firstChild();
407    
408                if (node != null) {
409                    if (node.is(new String[] { "SUPER_CTOR_CALL", "CTOR_CALL" })) {
410                        if (node.is("SUPER_CTOR_CALL")) {
411                            target.setSuperType(ConstructorDef.SUPER);
412                        }
413                        else {
414                            target.setSuperType(ConstructorDef.THIS);
415                        }
416    
417                        node = node.firstChild();
418                        node.ensure("ELIST");
419    
420                        if (!node.isLeaf()) {
421                            node = node.firstChild();
422    
423                            // Pull off EXPR siblings
424                            do {
425                                superParameter(target, node);
426                                node = node.nextSibling();
427                            }
428                            while (node != null);
429                        }
430                        // else we have a super() with-out/params
431                    }
432                }
433            }
434        }
435    
436        protected void superParameter(final ConstructorDef target, final Node parent) {
437            assert parent != null;
438    
439            parent.ensure("EXPR");
440    
441            Node node = parent.firstChild();
442    
443            SuperParameterDef def = new SuperParameterDef();
444    
445            if (node.is("TYPECAST")) {
446                node = node.firstChild();
447                assert node != null;
448    
449                node.ensure("TYPE");
450                def.setType(type(node));
451            }
452            else if (node.is("LITERAL_as")) {
453                node = node.firstChild();
454                assert node != null;
455    
456                node = node.nextSibling();
457                assert node != null;
458    
459                node.ensure("TYPE");
460                def.setType(type(node));
461            }
462            else if (node.is("LITERAL_new")) {
463                node = node.firstChild();
464                assert node != null;
465    
466                def.setType(type(node));
467            }
468            else if (node.is(new String[] { "LITERAL_true", "LITERAL_false" })) {
469                def.setType(TypeDef.BOOLEAN);
470            }
471            else if (node.is("STRING_LITERAL")) {
472                def.setType(TypeDef.STRING);
473            }
474            else if (node.is("NUM_INT")) {
475                def.setType(TypeDef.INT);
476            }
477            else if (node.is("NUM_LONG")) {
478                def.setType(TypeDef.LONG);
479            }
480            else if (node.is("NUM_FLOAT")) {
481                def.setType(TypeDef.FLOAT);
482            }
483            else if (node.is("NUM_DOUBLE")) {
484                def.setType(TypeDef.DOUBLE);
485            }
486            else if (node.is("NUM_BIG_INT")) {
487                def.setType(TypeDef.BIG_INT);
488            }
489            else if (node.is("NUM_BIG_DECIMAL")) {
490                def.setType(TypeDef.BIG_DECIMAL);
491            }
492            else if (node.is("STRING_CONSTRUCTOR")) {
493                def.setType(TypeDef.STRING);
494            }
495            else if (node.is("IDENT")) {
496                // Could be a reference to parameters
497                String ident = node.text();
498    
499                Set parameters = target.getParameters();
500                if (!parameters.isEmpty()) {
501                    Iterator iter = parameters.iterator();
502    
503                    while (iter.hasNext()) {
504                        ParameterDef param = (ParameterDef)iter.next();
505                        String name = param.getName();
506    
507                        if (name != null && name.equals(ident)) {
508                            def.setType(param.getType());
509                            break;
510                        }
511                    }
512                }
513            }
514            else {
515                // Lets just assume the parser has done its job, but we don't have type information
516                // so all we can do is use a null value w/o any cast
517                // throw new UnexpectedNodeException(node);
518            }
519    
520            target.addSuperParameter(def);
521        }
522    
523        protected void methodDef(final Node parent) {
524            assert parent != null;
525    
526            MethodDef def = new MethodDef();
527    
528            Node node = parent.firstChild();
529    
530            if (node.is("TYPE_PARAMETERS")) {
531                //
532                // FIXME: Support generics
533                //
534                node = node.nextSibling();
535            }
536    
537            node = modifiers(def, node);
538    
539            if (node.is("TYPE")) {
540                def.setReturns(type(node));
541                node = node.nextSibling();
542            }
543            else {
544                def.setReturns(new TypeDef());
545            }
546    
547            node = name(def, node);
548    
549            node = parameters(def, node);
550    
551            node = throwz(def, node);
552    
553            // Don't care about the body
554            if (node != null) {
555                node.skip("SLIST");
556            }
557    
558            javadocs(def, parent);
559    
560            clazz.addMethod(def);
561        }
562    
563        protected void annotationFieldDef(final Node parent) {
564            assert parent != null;
565            
566            // methodDef(parent);
567    
568            MethodDef def = new MethodDef();
569    
570            Node node = parent.firstChild();
571    
572            if (node.is("TYPE_PARAMETERS")) {
573                //
574                // FIXME: Support generics
575                //
576                node = node.nextSibling();
577            }
578    
579            node = modifiers(def, node);
580    
581            if (node.is("TYPE")) {
582                def.setReturns(type(node));
583                node = node.nextSibling();
584            }
585            else {
586                def.setReturns(new TypeDef());
587            }
588    
589            //
590            // TODO: Support "default"
591            //
592            
593            node = name(def, node);
594    
595            javadocs(def, parent);
596    
597            clazz.addMethod(def);
598        }
599        
600        protected void fieldDef(final Node parent) {
601            assert parent != null;
602    
603            FieldDef def = new FieldDef();
604    
605            Node node = parent.firstChild();
606    
607            node = modifiers(def, node);
608    
609            if (node.is("TYPE")) {
610                def.setType(type(node));
611                node = node.nextSibling();
612            }
613            else {
614                def.setType(new TypeDef());
615            }
616    
617            name(def, node);
618    
619            javadocs(def, parent);
620    
621            clazz.addField(def);
622        }
623    
624        protected void enumConstantDef(final Node parent) {
625            assert parent != null;
626    
627            parent.ensure("ENUM_CONSTANT_DEF");
628    
629            assert clazz instanceof EnumDef;
630            EnumDef def = (EnumDef)clazz;
631    
632            Node node = parent.firstChild();
633    
634            if (node.is("ANNOTATIONS")) {
635                node = node.nextSibling();
636            }
637    
638            String name = identifier(node);
639    
640            //
641            // TODO: Determine initialization expression
642            //
643    
644            /*
645            Expression init = null;
646            element = element.getNextSibling();
647            if (element!=null) {
648                init = expression(element);
649                if (isType(ELIST,element)) {
650                    if(init instanceof ListExpression && !((ListExpression)init).isWrapped()) {
651                        ListExpression le = new ListExpression();
652                        le.addExpression(init);
653                        init = le;
654                    }
655                }
656            }
657            */
658    
659            def.addConstant(name);
660        }
661    
662        //
663        // Helpers
664        //
665    
666        protected Node name(final NameAware target, final Node node) {
667            assert target != null;
668            assert node != null;
669    
670            target.setName(identifier(node));
671    
672            return node.nextSibling();
673        }
674    
675        protected Node modifiers(final ModifiersAware target, final Node parent) {
676            assert parent != null;
677    
678            // Skip unless this is a modifiers node
679            if (!parent.is("MODIFIERS")) {
680                return parent;
681            }
682    
683            ModifiersDef def = new ModifiersDef();
684    
685            for (Node node = parent.firstChild(); node != null; node = node.nextSibling()) {
686    
687                if (node.is(new String[] { "STRICTFP", "STATIC_IMPORT" })) {
688                    // ignore
689                }
690                if (node.is("ANNOTATION")) {
691                    //
692                    // FIXME: Add annotation support
693                    //
694                }
695                else if (node.is("LITERAL_private")) {
696                    def.add(ModifiersDef.PRIVATE);
697                }
698                else if (node.is("LITERAL_protected")) {
699                    def.add(ModifiersDef.PROTECTED);
700                }
701                else if (node.is("LITERAL_public")) {
702                    def.add(ModifiersDef.PUBLIC);
703                }
704                else if (node.is("ABSTRACT")) {
705                    def.add(ModifiersDef.ABSTRACT);
706                }
707                else if (node.is("FINAL")) {
708                    def.add(ModifiersDef.FINAL);
709                }
710                else if (node.is("LITERAL_native")) {
711                    def.add(ModifiersDef.NATIVE);
712                }
713                else if (node.is("LITERAL_static")) {
714                    def.add(ModifiersDef.STATIC);
715                }
716                else if (node.is("LITERAL_synchronized")) {
717                    def.add(ModifiersDef.SYNCHRONIZED);
718                }
719                else if (node.is("LITERAL_transient")) {
720                    def.add(ModifiersDef.TRANSIENT);
721                }
722                else if (node.is("LITERAL_volatile")) {
723                    def.add(ModifiersDef.VOLATILE);
724                }
725                else {
726                    throw new UnexpectedNodeException(node);
727                }
728            }
729    
730            target.getModifiers().merge(def);
731    
732            return parent.nextSibling();
733        }
734    
735        protected Set interfaces(final Node parent) {
736            assert parent != null;
737    
738            Set set = new LinkedHashSet();
739    
740            for (Node node = parent.firstChild(); node != null; node = node.nextSibling()) {
741                set.add(type(node));
742            }
743    
744            return set;
745        }
746    
747        protected Node throwz(final ThrowsAware target, final Node parent) {
748            assert target != null;
749            // assert parent != null;
750    
751            if (parent == null) {
752                return null;
753            }
754    
755            // The throw statement is optional, so skip if not present
756            if (!parent.is("LITERAL_throws")) {
757                return parent;
758            }
759    
760            Node node = parent.firstChild();
761    
762            if (node != null) {
763                do {
764                    target.getThrows().add(type(node));
765                    node = node.nextSibling();
766                }
767                while (node != null);
768            }
769    
770    
771            return parent.nextSibling();
772        }
773    
774        protected Node parameters(final ParametersAware target, final Node parent) {
775            assert target != null;
776            assert parent != null;
777    
778            parent.ensure("PARAMETERS");
779    
780            Node node = parent.firstChild();
781    
782            if (node != null) {
783                do {
784                    target.addParameter(parameter(node));
785                    node = node.nextSibling();
786                }
787                while (node != null);
788            }
789    
790            return parent.nextSibling();
791        }
792    
793        protected ParameterDef parameter(final Node parent) {
794            assert parent != null;
795    
796            ParameterDef def = new ParameterDef();
797    
798            Node node = parent.firstChild();
799    
800            node = modifiers(def, node);
801    
802            if (node.is("TYPE")) {
803                def.setType(type(node));
804                node = node.nextSibling();
805            }
806            else {
807                def.setType(new TypeDef());
808            }
809    
810            def.setName(identifier(node));
811    
812            return def;
813        }
814    
815        protected TypeDef type(final Node parent) {
816            assert parent != null;
817    
818            TypeDef def = new TypeDef();
819    
820            Node node;
821    
822            if (parent.is(new String[] { "IDENT", "DOT" })) {
823                node = parent;
824            }
825            else {
826                node = parent.firstChild();
827            }
828    
829            if (node != null) {
830                int dim = 0;
831    
832                // Determine array dimentions if there are any
833                while (node.is("ARRAY_DECLARATOR")) {
834                    node = node.firstChild();
835                    dim++;
836                }
837    
838                def.setDimensions(dim);
839                def.setName(qualifiedName(node));
840            }
841            
842            return def;
843        }
844    
845        private static JavaDocParser javaDocParser = new JavaDocParser();
846    
847        protected void javadocs(final JavaDocAware target, final Node node) {
848            assert target != null;
849            assert node != null;
850    
851            // Get the snippet between nodes
852            String text = parser.snippet(lastNode, node);
853    
854            // Remember where we last looked
855            lastNode = node;
856    
857            // Attempt to parse out the docs
858            JavaDocDef def = javaDocParser.parse(text);
859    
860            if (def != null) {
861                target.setJavaDoc(def);
862            }
863        }
864    
865        protected String qualifiedName(final Node parent) {
866            assert parent != null;
867    
868            if (parent.is("IDENT")) {
869                return parent.text();
870            }
871            else if (parent.is("DOT")) {
872                Node node = parent.firstChild();
873                StringBuffer buff = new StringBuffer();
874                boolean first = true;
875    
876                for (; node != null; node = node.nextSibling()) {
877                    if (first) {
878                        first = false;
879                    }
880                    else {
881                        buff.append(".");
882                    }
883    
884                    buff.append(qualifiedName(node));
885                }
886                return buff.toString();
887            }
888            else {
889                return parent.text();
890            }
891        }
892    
893        protected String identifier(final Node node) {
894            assert node != null;
895    
896            node.ensure("IDENT");
897    
898            return node.text();
899        }
900    }