/* * Copyright 2012 Red Hat, Inc. and/or its affiliates. * * Licensed under the Eclipse Public License version 1.0, available at * http://www.eclipse.org/legal/epl-v10.html */ package org.jboss.forge.roaster; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.ServiceLoader; import org.jboss.forge.roaster.model.JavaType; import org.jboss.forge.roaster.model.JavaUnit; import org.jboss.forge.roaster.model.source.JavaSource; import org.jboss.forge.roaster.spi.FormatterProvider; import org.jboss.forge.roaster.spi.JavaParser; /** * Responsible for parsing data into new {@link JavaType} instances. * * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> */ public final class Roaster { private Roaster() { } private static List<JavaParser> parsers; private static List<FormatterProvider> formatters; private static List<JavaParser> getParsers() { synchronized (Roaster.class) { if (parsers == null || parsers.isEmpty()) { parsers = new ArrayList<JavaParser>(); for (JavaParser p : ServiceLoader.load(JavaParser.class, Roaster.class.getClassLoader())) { parsers.add(p); } } if (parsers.isEmpty()) { throw new IllegalStateException("No instances of [" + JavaParser.class.getName() + "] were found on the classpath."); } } return parsers; } private static List<FormatterProvider> getFormatters() { synchronized (Roaster.class) { if (formatters == null || formatters.isEmpty()) { formatters = new ArrayList<FormatterProvider>(); for (FormatterProvider p : ServiceLoader.load(FormatterProvider.class, Roaster.class.getClassLoader())) { formatters.add(p); } } if (formatters.isEmpty()) { throw new IllegalStateException("No instances of [" + FormatterProvider.class.getName() + "] were found on the classpath."); } } return formatters; } /** * Create a new empty {@link JavaSource} instance. */ public static <T extends JavaSource<?>> T create(final Class<T> type) { for (JavaParser parser : getParsers()) { final T result = parser.create(type); if (result != null) { return result; } } throw new ParserException("Cannot find JavaParserProvider capable of producing JavaSource of type " + type.getSimpleName(), new IllegalArgumentException(type.getName())); } /** * Open the given {@link File}, parsing its contents into a new {@link JavaType} instance. */ public static JavaType<?> parse(final File file) throws FileNotFoundException { return parse(JavaType.class, file); } /** * Parse the given {@link URL} data into a new {@link JavaType} instance. */ public static JavaType<?> parse(final URL data) throws IOException { return parse(JavaType.class, data); } /** * Read the given {@link InputStream} and parse the data into a new {@link JavaType} instance. */ public static JavaType<?> parse(final InputStream data) { return parse(JavaType.class, data); } /** * Parse the given character array into a new {@link JavaType} instance. */ public static JavaType<?> parse(final char[] data) { return parse(JavaType.class, data); } /** * Parse the given String data into a new {@link JavaType} instance. */ public static JavaType<?> parse(final String data) { return parse(JavaType.class, data); } /** * Read the given {@link URL} and parse its data into a new {@link JavaType} instance of the given type. * * @throws FileNotFoundException */ public static <T extends JavaType<?>> T parse(final Class<T> type, final URL url) throws IOException { return internalParse(type, url.openStream()); } /** * Read the given {@link File} and parse its data into a new {@link JavaType} instance of the given type. * * @throws FileNotFoundException */ public static <T extends JavaType<?>> T parse(final Class<T> type, final File file) throws FileNotFoundException { return internalParse(type, new FileInputStream(file)); } /** * Read the given character array and parse its data into a new {@link JavaType} instance of the given type. */ public static <T extends JavaType<?>> T parse(final Class<T> type, final char[] data) { return parse(type, new String(data)); } /** * Validates a code snippet and returns a {@link List} of {@link Problem}. Never returns <code>null</code>. * * @param snippet any Java code * @throws ParserException if no {@link JavaParser} implementation could be found */ public static List<Problem> validateSnippet(String snippet) throws ParserException { for (JavaParser parser : getParsers()) { return parser.validateSnippet(snippet); } throw new ParserException("Cannot find JavaParser capable of validating the requested data"); } /** * Read the given string and parse its data into a new {@link JavaType} instance of the given type. */ @SuppressWarnings("unchecked") public static <T extends JavaType<?>> T parse(final Class<T> type, final String data) { for (JavaParser parser : getParsers()) { final JavaUnit unit = parser.parseUnit(data); if (type.isInstance(unit.getGoverningType())) { return (T) unit.getGoverningType(); } else if (unit != null) { throw new ParserException("Source does not represent a [" + type.getSimpleName() + "], instead was [" + unit.getGoverningType().getClass().getSimpleName() + "] - Cannot convert."); } } throw new ParserException("Cannot find JavaParser capable of parsing the requested data"); } /** * Read the given {@link InputStream} and parse its data into a new {@link JavaType} instance of the given type. The * caller is responsible for closing the stream. */ public static <T extends JavaType<?>> T parse(final Class<T> type, final InputStream data) { return parse(type, Streams.toString(data)); } /** * Read the given {@link String} and parse its data into a new {@link JavaUnit} instance of the given type. */ public static JavaUnit parseUnit(final String data) { for (JavaParser parser : getParsers()) { final JavaUnit unit = parser.parseUnit(data); if (unit != null) return unit; } throw new ParserException("Cannot find JavaParser capable of parsing the requested data"); } /** * Read the given {@link InputStream} and parse its data into a new {@link JavaUnit} instance of the given type. The * caller is responsible for closing the stream. */ public static JavaUnit parseUnit(final InputStream data) { return parseUnit(Streams.toString(data)); } /** * Format the given {@link String} as a Java source file, using the built in code format style. * * @param source a java source code * @return the formatted source code */ public static String format(String source) { String result = source; for (FormatterProvider formatter : getFormatters()) { result = formatter.format(result); } return result; } /** * Format the given {@link String} as a Java source type, using the given code format {@link Properties} */ public static String format(Properties properties, String source) { String result = source; for (FormatterProvider formatter : getFormatters()) { result = formatter.format(properties, result); } return result; } private static <T extends JavaType<?>> T internalParse(final Class<T> type, final InputStream data) { try { return parse(type, data); } finally { Streams.closeQuietly(data); } } }