All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.code.configprocessor.ConfigProcessor Maven / Gradle / Ivy

Go to download

Generates configuration files modified according to a ruleset to prepare them for different environments

There is a newer version: 2.3
Show newest version
/*
 * Copyright (C) 2009 Leandro de Oliveira Aparecido 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.configprocessor;

import static com.google.code.configprocessor.util.IOUtils.*;
import static org.apache.commons.lang.StringUtils.*;

import java.io.*;
import java.util.*;

import org.apache.commons.lang.*;
import org.apache.tools.ant.*;

import com.google.code.configprocessor.expression.*;
import com.google.code.configprocessor.io.*;
import com.google.code.configprocessor.log.*;
import com.google.code.configprocessor.maven.*;
import com.google.code.configprocessor.parsing.*;
import com.google.code.configprocessor.processing.*;
import com.google.code.configprocessor.processing.properties.*;
import com.google.code.configprocessor.processing.xml.*;

public class ConfigProcessor {

	private static final String DEFAULT_ENCODING = "UTF-8";

	private String encoding;
	private int lineWidth;
	private int indentSize;
	private Map namespaceContexts;
	private boolean useOutputDirectory;
	private File baseDir;
	private File outputDirectory;
	private LogAdapter log;
	private FileResolver fileResolver;
	private List parserFeatures;
	private boolean failOnMissingXpath;

	private File actualOutputDirectory;

	public ConfigProcessor(String encoding,
						   int indentSize,
						   int lineWidth,
						   Map namespaceContexts,
						   File baseDir,
						   File outputDirectory,
						   boolean useOutputDirectory,
						   LogAdapter log,
						   FileResolver fileResolver,
						   List parserFeatures,
						   boolean failOnMissingXpath) {
		this.encoding = encoding;
		this.indentSize = indentSize;
		this.lineWidth = lineWidth;
		this.namespaceContexts = namespaceContexts;
		this.baseDir = baseDir;
		this.outputDirectory = outputDirectory;
		this.useOutputDirectory = useOutputDirectory;
		this.log = log;
		this.fileResolver = fileResolver;
		this.parserFeatures = parserFeatures;
		this.failOnMissingXpath = failOnMissingXpath;
	}

	public void init() throws IOException {
		if (useOutputDirectory) {
			if (!outputDirectory.exists()) {
				outputDirectory.mkdirs();
			}
			actualOutputDirectory = outputDirectory;
		}
		if (encoding == null) {
			getLog().warn("Encoding has not been set, using default [" + DEFAULT_ENCODING + "].");
			encoding = DEFAULT_ENCODING;
		}

		getLog().debug("Using output directory [" + actualOutputDirectory + "]");
		getLog().debug("File encodig is [" + encoding + "]");
	}

	public void execute(ExpressionResolver resolver, Transformation transformation) throws ConfigProcessorException, IOException {
		String input = transformation.getInput();
		Action action = getAction(transformation);
		String configIdentifier = getConfigIdentifier(transformation);

		if (input != null && input.contains("*")) {
			// input parameter specifies a wildcard pattern
			if (!StringUtils.isBlank(transformation.getOutput())) {
				throw new ConfigProcessorException("Cannot specify output file if wildcard pattern based input is given");
			}
			getLog().info("Using wildcard pattern based input [" + input + "]");
			List inputFiles = getMatchingFiles(input);
			for (File inputFile : inputFiles) {
				String type = getInputType(transformation, inputFile);
				File outputFile;
				if (actualOutputDirectory != null) {
					// calculate a relative path below the output directory based on the input file
					outputFile = new File(actualOutputDirectory, baseDir.toURI().relativize(inputFile.toURI()).getPath());
					createOutputFile(outputFile);
				} else {
					outputFile = inputFile;
				}
				process(resolver, inputFile.getPath(), inputFile, outputFile, configIdentifier, action, type);
			}
		} else {
			File inputFile = fileResolver.resolve(transformation.getInput());
			if (!inputFile.exists()) {
				throw new ConfigProcessorException("Input file [" + inputFile + "] does not exist");
			}
			// use input file as output file if output is not set
			File output;
			if (StringUtils.isBlank(transformation.getOutput())) {
				output = inputFile;
			} else {
				output = new File(actualOutputDirectory, transformation.getOutput());
				createOutputFile(output);
			}
			String type = getInputType(transformation, inputFile);
			process(resolver, transformation.getInput(), inputFile, output, configIdentifier, action, type);
		}
	}

	protected Action getAction(Transformation transformation) throws ConfigProcessorException, IOException {
		if (transformation.getConfig() == null && transformation.getRules() == null) {
			throw new ConfigProcessorException("Transformation config file or rules must be set");
		} else if (transformation.getConfig() != null && transformation.getRules() != null) {
			throw new ConfigProcessorException("Cannot specify transformation config file and rules at the same time");
		}

		Action action;
		Reader configReader;
		if (transformation.getConfig() == null) {
			StringWriter writer = new StringWriter();
			AntrunXmlPlexusConfigurationWriter xmlWriter = new AntrunXmlPlexusConfigurationWriter();
			xmlWriter.write(transformation.getRules(), writer);
			String configContent = trimToNull(writer.toString());
			configContent = removeStart(configContent, "");
			configContent = removeEnd(configContent, "");
			StringBuilder sb = new StringBuilder(configContent.length() + XmlHelper.ROOT_PROCESSOR_START.length() + XmlHelper.ROOT_PROCESSOR_END.length());
			sb.append(XmlHelper.ROOT_PROCESSOR_START);
			sb.append(configContent);
			sb.append(XmlHelper.ROOT_PROCESSOR_END);
			configReader = new StringReader(sb.toString());
		} else {
			File config = fileResolver.resolve(transformation.getConfig());

			if (!config.exists()) {
				throw new ConfigProcessorException("Configuration file [" + config + "] does not exist");
			}

			configReader = new InputStreamReader(new FileInputStream(config), encoding);
		}

		try {
			ProcessingConfigurationParser parser = new ProcessingConfigurationParser();
			action = parser.parse(configReader);
		} catch (ParsingException e) {
			throw new ConfigProcessorException("Error parsing transformation config [" + getConfigIdentifier(transformation) + "]", e);
		} finally {
			close(configReader, getLog());
		}
		action.validate();

		return action;
	}

	protected String getConfigIdentifier(Transformation transformation) throws ConfigProcessorException {
		if (transformation.getConfig() == null) {
			StringWriter writer = new StringWriter();
			AntrunXmlPlexusConfigurationWriter xmlWriter = new AntrunXmlPlexusConfigurationWriter();
			try {
				xmlWriter.write(transformation.getRules(), writer);
			} catch (IOException e) {
				throw new ConfigProcessorException("Shouldn't happen because there is no I/O here", e);
			}
			return abbreviate(trimToNull(removeStart(writer.toString(), "")), 100);
		}
		return transformation.getConfig();
	}

	/**
	 * Scans all files below the given baseDirectory using the supplied pattern.
	 * All files matching the pattern are returned.
	 * The implementation is utilizing {@link DirectoryScanner} for pattern matching, e.g.
	 * it allows to use single ("*") and double wildcards ("**") for matching
	 * arbitrary characters or directories.
	 *
	 * Examples:
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
* *
	 * *.xml
	 * 
* *
matches all XML files in the base directory
* *
	 * **\/*.xml
	 * 
* *
matches all XML files in any subfolder
* * @param pattern the directory and file name pattern that files shall match * @return the {@link List} of {@link File}s that match the given pattern * @throws ConfigProcessorException */ protected List getMatchingFiles(String pattern) throws ConfigProcessorException { if (pattern == null || pattern.length() == 0) { throw new ConfigProcessorException("Invalid pattern [" + pattern + "]"); } DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir(baseDir); scanner.setIncludes(new String[] { pattern }); scanner.setCaseSensitive(false); scanner.scan(); String[] fileNames = scanner.getIncludedFiles(); List files = new ArrayList(); for (String fileName : fileNames) { files.add(new File(baseDir, fileName)); } return files; } /** * Detects input file type. * If {@link com.google.code.configprocessor.Transformation#getType()} is not null, * this type is used, otherwise it is tried to guess the type from the given * input File based on the file extension (.properties or .xml). * If no type could be found or guessed, {@link Transformation#XML_TYPE} is used. * * @param transformation which can have an explicit type set * @param input file from which the type can be guessed if transformation parameter does not * contain a type * @return Input file type. */ protected String getInputType(Transformation transformation, File input) { String type; if (transformation.getType() == null) { if (input.getName().endsWith(".properties")) { type = Transformation.PROPERTIES_TYPE; } else if (input.getName().endsWith(".xml")) { type = Transformation.XML_TYPE; } else { if (getLog() != null) { getLog().warn( "Could not auto-detect type of input [" + input + "], assuming it is XML. It is recommended that you configure it in your pom.xml (tag: transformations/transformation/type) to avoid errors"); } type = Transformation.XML_TYPE; } } else { type = transformation.getType(); } return type; } /** * Processes a file. * * @param resolver * @param inputName Symbolic name of the input file to read from. * @param input Input file to read from. * @param output Output file to write to. * @param configName Symbolic name of the file containing rules to process the input. * @param config File containing rules to process the input. * @param type Type of the input file. Properties, XML or null if it is to be auto-detected. * @throws ConfigProcessorException If processing cannot be performed. */ protected void process(ExpressionResolver resolver, String inputName, File input, File output, String configName, Action action, String type) throws ConfigProcessorException { getLog().info("Processing file [" + inputName + "] using config [" + configName + "], outputing to [" + output + "]"); InputStream inputStream = null; ByteArrayOutputStream outputStream = null; InputStreamReader inputStreamReader = null; OutputStreamWriter outputStreamWriter = null; try { inputStream = new FileInputStream(input); outputStream = new ByteArrayOutputStream(); inputStreamReader = new InputStreamReader(inputStream, encoding); outputStreamWriter = new OutputStreamWriter(outputStream, encoding); ActionProcessor processor = getActionProcessor(resolver, type); processor.process(inputStreamReader, outputStreamWriter, action); } catch (ParsingException e) { throw new ConfigProcessorException("Error processing file [" + inputName + "] using configuration [" + configName + "]", e); } catch (IOException e) { throw new ConfigProcessorException("Error reading/writing files. Input is [" + inputName + "], configuration is [" + configName + "]", e); } finally { close(inputStreamReader, getLog()); } FileOutputStream fileOut = null; try { fileOut = new FileOutputStream(output); outputStream.writeTo(fileOut); } catch (FileNotFoundException e) { getLog().error("Error opening file [" + output + "]", e); } catch (IOException e) { getLog().error("Error writing file [" + output + "]", e); } finally { close(outputStreamWriter, getLog()); close(fileOut, getLog()); } } /** * Obtain the action processor for the input. * * @param expressionResolver * @param type Type of the input file. Properties or XML. * @return ActionProcessor for the input file. * @throws ConfigProcessorException If processing cannot be performed. */ protected ActionProcessor getActionProcessor(ExpressionResolver expressionResolver, String type) throws ConfigProcessorException { if (Transformation.XML_TYPE.equals(type)) { return new XmlActionProcessor(encoding, lineWidth, indentSize, fileResolver, expressionResolver, namespaceContexts, parserFeatures, failOnMissingXpath); } else if (Transformation.PROPERTIES_TYPE.equals(type)) { return new PropertiesActionProcessor(encoding, fileResolver, expressionResolver); } else { throw new ConfigProcessorException("Unknown file type [" + type + "]"); } } /** * Creates output file and required directories. * * @param output Output file to create. * @throws ConfigProcessorException If processing cannot be performed. */ protected void createOutputFile(File output) throws ConfigProcessorException { try { File directory = output.getParentFile(); getLog().debug(output.toString()); if (!directory.exists()) { forceMkdirs(output.getParentFile()); } } catch (IOException e) { throw new ConfigProcessorException(e.getMessage(), e); } } public LogAdapter getLog() { return log; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy