summaryrefslogtreecommitdiffstats
path: root/args4j/args4j-tools/src/org/kohsuke/args4j/apt/AnnotationProcessorFactoryImpl.java
blob: d7148f89335ceaac1471dcac9a10881309f983fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package org.kohsuke.args4j.apt;

import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.AnnotationProcessorFactory;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.ClassDeclaration;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.FieldDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.declaration.MemberDeclaration;
import com.sun.mirror.type.ClassType;
import com.sun.mirror.util.SimpleDeclarationVisitor;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Properties;

/**
 * {@link AnnotationProcessorFactory} to be invoked by APT.
 *
 * This class receives options from the Main method through system properties (ouch!).
 *
 * @author Kohsuke Kawaguchi
 */
public class AnnotationProcessorFactoryImpl implements AnnotationProcessorFactory {

    private File outDir;
    private String format;
    private Properties resource = null;

    public AnnotationProcessorFactoryImpl() {
        outDir = new File(System.getProperty("args4j.outdir"));
        format = System.getProperty("args4j.format");

        String res = System.getProperty("args4j.resource");
        if(res!=null && res.length()>0) {
            try {
                resource = new Properties();
                resource.load(new FileInputStream(res));
            } catch (IOException e) {
                throw new Error(e);
            }
        }
    }

    public Collection<String> supportedOptions() {
        return Collections.emptyList();
    }

    public Collection<String> supportedAnnotationTypes() {
        return Arrays.asList(Option.class.getName(),Argument.class.getName());
    }

    public AnnotationProcessor getProcessorFor(final Set<AnnotationTypeDeclaration> annotationTypeDeclarations, final AnnotationProcessorEnvironment env) {
        return new AnnotationProcessor() {
            public void process() {
                Collection<Declaration> params = env.getDeclarationsAnnotatedWith((AnnotationTypeDeclaration)env.getTypeDeclaration(Option.class.getName()));

                final Set<TypeDeclaration> optionBeans = new HashSet<TypeDeclaration>();
                for (Declaration d : params) {
                    d.accept(new SimpleDeclarationVisitor() {
                        public void visitFieldDeclaration(FieldDeclaration f) {
                            TypeDeclaration dt = f.getDeclaringType();
                            optionBeans.add(dt);
                        }

                        public void visitMethodDeclaration(MethodDeclaration m) {
                            optionBeans.add(m.getDeclaringType());
                        }
                    });
                }

                for (TypeDeclaration t : optionBeans) {
                    // make sure that they are on classes
                    if(t instanceof ClassDeclaration) {
                        ClassDeclaration cd = (ClassDeclaration)t;
                        try {
                            AnnotationVisitor writer = createAnnotationVisitor(cd);
                            env.getMessager().printNotice("Processing "+cd.getQualifiedName());
                            scan(cd, writer);
                        } catch (IOException e) {
                            env.getMessager().printError(e.getMessage());
                        }
                    } else {
                        env.getMessager().printError(t.getPosition(),
                            "args4j annotations need to be placed on a class");
                    }
                }
            }
        };
    }

    private AnnotationVisitor createAnnotationVisitor(ClassDeclaration cd) throws IOException {
        FileWriter out = new FileWriter(new File(outDir,cd.getQualifiedName()+"."+format.toLowerCase()));
        AnnotationVisitor writer;
        if(format.equals("XML"))
            writer = new XmlWriter(out,cd);
        else if (format.equals("TXT"))
            writer = new TxtWriter(out, cd);
        else
            writer = new HtmlWriter(out);
        return new AnnotationVisitorReorderer(writer);
    }

    private void scan(ClassDeclaration decl, AnnotationVisitor visitor) {
        while(decl!=null) {
            for( FieldDeclaration f : decl.getFields() )
                scan(f, visitor);

            for (MethodDeclaration m : decl.getMethods())
                scan(m, visitor);

            ClassType sc = decl.getSuperclass();
            if(sc==null)    break;

            decl = sc.getDeclaration();
        }

        visitor.done();
    }

    private void scan(MemberDeclaration f, AnnotationVisitor visitor) {
        Option o = f.getAnnotation(Option.class);
        if(o==null) return;

        String usage = getUsage(o);
        if(isOptionHidden(usage))    return;

        visitor.onOption(new OptionWithUsage(o, usage));
    }

    private boolean isOptionHidden(String usage) {
        return usage==null || usage.length()==0;
    }

    private String getUsage(Option o) {
        if(resource==null)
            return o.usage();
        else
            return resource.getProperty(o.usage());
    }

}