/*******************************************************************************
* Copyright (c) 2005, 2009 committers of openArchitectureWare and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* committers of openArchitectureWare - initial API and implementation
*******************************************************************************/
package org.eclipse.xpand2.output;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.mwe.core.resources.ResourceLoaderFactory;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
/**
* Formats generated Java code using the Eclipse code formatter.
*
* @author Sven Efftinge (http://www.efftinge.de) (initial)
* @author Bernd Kolb
* @author Peter Friese
* @author Markus Voelter
* @author Michael Leopoldseder
* @author Karsten Thoms
* @since 4.0
*/
public class JavaBeautifier implements PostProcessor {
/** Logger instance. */
private final Log log = LogFactory.getLog(getClass());
/** Singleton code formatter instance. */
private CodeFormatter codeFormatter;
/**
* Path to the config file for the formatter. See
* http://www.peterfriese.de/index.php/2007/05/28/formatting-your-code-using-the-eclipse-code-formatter/
* for more information on creating the config file.
*/
private String configFile;
private Properties options;
/**
* Formats the file using Eclipse code formatter. The file must have the
* extension '.java'.
*/
public void beforeWriteAndClose(final FileHandle info) {
if (info.getAbsolutePath() != null && info.getAbsolutePath().endsWith(".java")) {
IDocument doc = new Document(info.getBuffer().toString());
TextEdit edit = getCodeFormatter().format(CodeFormatter.K_COMPILATION_UNIT, doc.get(), 0,
doc.get().length(), 0, null);
// check if text formatted successfully
if (edit != null) {
try {
edit.apply(doc);
info.setBuffer(new StringBuffer(doc.get()));
} catch (MalformedTreeException e) {
log.warn("Error during code formatting. Illegal code edit tree (" + e.getMessage() + ").");
} catch (BadLocationException e) {
log.warn("Error during code formatting. Bad location (" + e.getMessage() + ").");
}
} else {
log.warn("File " + info.getAbsolutePath()
+ " could not be formatted. Make sure your template produces legal Java code!");
}
}
}
/**
* Returns an instance of the Eclipse code formatter. If the user supplied
* the path to a config file, this file will be used to configure the code
* formatter. Otherwise we use the default options supplied with Xpand.
*
* @return a preconfigured instance of the Eclipse code formatter.
*/
private CodeFormatter getCodeFormatter() {
if (codeFormatter == null) {
if ( configFile == null ) {
options = new Properties();
options.put("org.eclipse.jdt.core.compiler.compliance","1.5");
options.put("org.eclipse.jdt.core.compiler.codegen.targetPlatform","1.5");
options.put("org.eclipse.jdt.core.compiler.source","1.5");
log.debug("no config file specified; using the default config file supplied with Xpand: org.eclipse.jdt.core.formatterprefs");
} else {
options = readConfig(configFile);
}
// instantiate the formatter
codeFormatter = ToolFactory.createCodeFormatter(options);
}
return codeFormatter;
}
/**
* Return a Java Properties file representing the options that are in the
* specified config file.
*/
private Properties readConfig(String filename) {
BufferedInputStream stream = null;
BufferedReader reader = null;
try {
InputStream is = openStream(filename);
final Properties formatterOptions = new Properties();
if ( filename.endsWith(".xml")) {
Pattern pattern = Pattern.compile("<setting id=\"([^\"]*)\" value=\"([^\"]*)\"\\/>");
reader = new BufferedReader(new InputStreamReader(is));
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
Matcher matcher = pattern.matcher(line);
if ( matcher.matches() ) {
formatterOptions.put(matcher.group(1), matcher.group(2));
}
}
}
else {
stream = new BufferedInputStream(is);
formatterOptions.load(stream);
}
// add some settings for the compiler options
// which are not included in the Eclipse code style settings
// to make the code formatter working
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=222736
if( formatterOptions.get("org.eclipse.jdt.core.compiler.compliance") == null )
formatterOptions.put("org.eclipse.jdt.core.compiler.compliance", "1.5");
if( formatterOptions.get("org.eclipse.jdt.core.compiler.codegen.targetPlatform") == null )
formatterOptions.put("org.eclipse.jdt.core.compiler.codegen.targetPlatform", "1.5");
if( formatterOptions.get("org.eclipse.jdt.core.compiler.source") == null )
formatterOptions.put("org.eclipse.jdt.core.compiler.source", "1.5");
return formatterOptions;
} catch (IOException e) {
log.warn("Problem reading code formatter config file (" + e.getMessage() + ").");
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
/* ignore */
}
}
if ( reader != null ) {
try {
reader.close();
} catch (IOException e) {
/* ignore */
}
}
}
return null;
}
/**
* Searches for the given filename as a resource and returns a stream on it. Throws an IOException, if the file
* cannot be found.
*
* @param filename
* The name of the file to be searched in the resources.
* @return InputStream for subsequent reading
* @throws IOException
*/
protected InputStream openStream(String filename) throws IOException {
InputStream is = ResourceLoaderFactory.createResourceLoader().getResourceAsStream(filename);
if (is == null) {
throw new IOException("Config file [" + filename + "] does not exist.");
}
return is;
}
/**
* @return the configuration file for the formatter
*/
public String getConfigFile() {
return configFile;
}
/**
* @param configFile
* configuration file for the formatter
*/
public void setConfigFile(String configFile) {
if (configFile==null || "".equals(configFile.trim())) {
log.debug("Empty value for 'configValue' was set. Default settings will be used for formatting.");
this.configFile = null;
} else {
this.configFile = configFile;
}
}
/**
* {@inheritDoc}
*/
public void afterClose(final FileHandle impl) {
// do nothing here
}
}