/*
 * @(#)OptionProcessor.java	1.0 06/05/27
 * 
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 * 
 * -Redistribution in binary form must reproduce the above copyright notice, 
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL 
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST 
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY 
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, 
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * 
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */

/*
 * OptionProcessor.java
 *
 * Created on April 10, 2006, 8:35 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package options;

import java.io.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.tools.Diagnostic;


@SupportedAnnotationTypes({ "options.Option" })
public class OptionProcessor
    //TODO extends AbstractProcessor
    extends AbstractProcessor {
    
    //Global variable to hole the name of the annotation processor class name
    private String className = null;
    
    /** Creates a new instance of OptionProcessor */
    public OptionProcessor() {
    }
    
    //TODO add process() method
    public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
        
		  //processingEnv is a predefined member in AbstractProcessor class
		  //Messager allows the processor to output message to the environment
		  Messager messager = processingEnv.getMessager();
        
    	  //Create a hashtable to hold the option switch to option bean mapping
        HashMap<String, String> values = new HashMap<String, String>();
        
        //Loop through the annotations that we are going to process
        //In this case there should only be on: Option
        for (TypeElement te: elements)            
            //Get the members that are annotated with Option
            for(Element e: env.getElementsAnnotatedWith(te))
                //Process the members
                processAnnotation(e, values, messager);
            
        if (values.size() > 0)
            try {
                //Generate the option process class
                generateOptionProcessor(processingEnv.getFiler(), values);                
            } catch (Exception e) {
                messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage());
            }
        
        return (true);
    }
    
    //TODO add processAnnotation() method
    private void processAnnotation(Element element, HashMap<String, String> values, Messager msg) {
        //Get the Option annotation on the member
        Option opt = element.getAnnotation(Option.class);
        
        //Get the class name of the option bean
        className = element.getEnclosingElement().toString();
        
        //Check if the type in the member is a String. If not we ignore it
        if (!element.asType().toString().equals(String.class.getName())) {
            msg.printMessage(Diagnostic.Kind.WARNING
                , element.asType() + " not supported. " + opt.name() + " not processed");
            return;
        }
        
        //Save the option switch and the member's name in a hash set
        //Eg. -filename (option switch) mapped to fileName (member)
        values.put(opt.name(), element.getSimpleName().toString());
    }
    
    //OptionProcessor code generator
    private void generateOptionProcessor(Filer filer, HashMap<String, String> values) throws Exception {
        
        String generatedClassName = className + "Processor";
        
        Writer writer = filer.createSourceFile(generatedClassName);
        
        writer.write("/* Generated on " + new Date() + " */\n");
        
        writer.write("public class " + generatedClassName + " {\n");
        
        writer.write("\tpublic static " + className + " process(String[] args) {\n");
        
        writer.write("\t\t" + className + " options = new " + className + "();\n");
        writer.write("\t\tint idx = 0;\n");
        
        writer.write("\t\twhile (idx < args.length) {\n");
        
        for (String key: values.keySet()) {
            writer.write("\t\t\tif (args[idx].equals(\"" + key + "\")) {\n");
            writer.write("\t\t\t\toptions." + values.get(key) + " = args[++idx];\n");
            writer.write("\t\t\t\tidx++;\n");
            writer.write("\t\t\t\tcontinue;\n");
            writer.write("\t\t\t}\n");
        }
        
        writer.write("\t\t\tSystem.err.println(\"Unknown option: \" + args[idx++]);\n");
        
        writer.write("\t\t}\n");
        
        writer.write("\t\treturn (options);\n");
        writer.write("\t}\n");
        
        writer.write("}");
        
        writer.flush();
        writer.close();
    }        
}
