001    package nl.tudelft.tbm.eeni.owl2java.formatter;
002    
003    import org.apache.commons.logging.Log;
004    import org.apache.commons.logging.LogFactory;
005    import org.eclipse.jdt.core.JavaCore;
006    import org.eclipse.jdt.core.ToolFactory;
007    import org.eclipse.jface.text.Document;
008    import org.eclipse.jface.text.IDocument;
009    import org.eclipse.text.edits.TextEdit;
010    
011    import java.io.FileNotFoundException;
012    import java.io.IOException;
013    import java.io.InputStream;
014    import java.util.Properties;
015    
016    /**
017     * Wrapper class for Eclipse JDT code formatter
018     */
019    public class CodeFormatter {
020        // Location of default settings file, relative to this class
021        private final static String DEFAULT_SETTINGS_FILE = "formatter-defaults.properties";
022    
023        // Log message emitter
024        private static Log log = LogFactory.getLog(CodeFormattingWriter.class);
025    
026        // Settings map for JDT code formatter
027        private Properties formatterSettings;
028    
029    
030        /**
031         * Constructor for CodeFormatter with default options
032         */
033        public CodeFormatter() {
034            this(null);
035        }
036    
037        /**
038         * Constructor for CodeFormatter with given settings;
039         * if given null, loads default formatter settings
040         */
041        public CodeFormatter(Properties formatterSettings) {
042            setFormatterSettings(formatterSettings);
043        }
044    
045        /**
046         * Formats source code using current formatter settings
047         */
048        public String format(String code) {
049    
050            /*
051               * The settings map must at least contain these three options, or the JDT code formatter will not work.
052               * Check if these settings exist, otherwise emit a warning and return the unformatted source code.
053               */
054            if (!formatterSettings.containsKey(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM)) {
055                log.warn("Code formatter settings must define " + JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM);
056                return code;
057            }
058            if (!formatterSettings.containsKey(JavaCore.COMPILER_COMPLIANCE)) {
059                log.warn("Code formatter settings must define " + JavaCore.COMPILER_COMPLIANCE);
060                return code;
061            }
062            if (!formatterSettings.containsKey(JavaCore.COMPILER_SOURCE)) {
063                log.warn("Code formatter settings must define " + JavaCore.COMPILER_SOURCE);
064                return code;
065            }
066    
067            // Load the code into an eclipse document
068            IDocument document = new Document();
069            document.set(code);
070    
071            // Create the code formatter
072            org.eclipse.jdt.core.formatter.CodeFormatter codeFormatter = ToolFactory.createCodeFormatter(this.formatterSettings);
073    
074            /*
075               *  Run the formatter and apply the modifications to the document.
076               *  If it fails (probably due to a syntax error), just return the unformatted source code.
077               */
078            TextEdit modifications = codeFormatter.format(org.eclipse.jdt.core.formatter.CodeFormatter.K_COMPILATION_UNIT,
079                    code, 0, code.length(), 0, null);
080            if (modifications != null) {
081                try {
082                    modifications.apply(document);
083                } catch (Exception e) {
084                    throw new RuntimeException(e);
085                }
086            }
087    
088            // Return the document contents as string
089            return document.get();
090        }
091    
092        /**
093         * Returns a Properties object representing containing all the code formatter options
094         */
095        public Properties getFormatterSettings() {
096            return formatterSettings;
097        }
098    
099        /**
100         * Sets the formatter options;
101         * if given null, reverts to default formatter options
102         */
103        public void setFormatterSettings(Properties formatterSettings) {
104            if (formatterSettings != null) {
105                this.formatterSettings = formatterSettings;
106            } else {
107                this.formatterSettings = getDefaultSettings();
108            }
109        }
110    
111        /**
112         * Loads default options from DEFAULT_OPTIONS_FILE
113         */
114        private static Properties getDefaultSettings() {
115            try {
116                InputStream inputStream = CodeFormattingWriter.class.getResourceAsStream(DEFAULT_SETTINGS_FILE);
117                if (inputStream != null) {
118                    Properties options = new Properties();
119                    options.load(inputStream);
120                    return options;
121                } else {
122                    throw new FileNotFoundException(DEFAULT_SETTINGS_FILE);
123                }
124            } catch (IOException e) {
125                throw new RuntimeException(e);
126            }
127        }
128    }