package com.sun.tools.xjc.addon.xew.config; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import javax.xml.namespace.QName; import com.sun.tools.xjc.BadCommandLineException; import com.sun.tools.xjc.Options; import com.sun.tools.xjc.addon.xew.config.CommonConfiguration.ConfigurationOption; import com.sun.tools.xjc.model.CCustomizations; import com.sun.tools.xjc.model.CPluginCustomization; import com.sun.tools.xjc.outline.Outline; import org.apache.commons.logging.LogFactory; import org.jvnet.jaxb2_commons.plugin.AbstractParameterizablePlugin; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; /** * Plugin base class that only contains code for plugin initalization and logging. * * @author <a href="mailto:dkatsubo@epo.org">Dmitry Katsubo</a> */ public abstract class AbstractConfigurablePlugin extends AbstractParameterizablePlugin { private static final String PLUGIN_NAME = "Xxew"; private static final QName XEW_QNAME = new QName( "http://github.com/jaxb-xew-plugin", "xew"); protected GlobalConfiguration globalConfiguration = new GlobalConfiguration(); public static final String COMMONS_LOGGING_LOG_LEVEL_PROPERTY_KEY = "org.apache.commons.logging.simplelog.defaultlog"; public AbstractConfigurablePlugin() { // Reset logger in parent because it should be re-initialized with correct loglevel threshold: logger = null; } @Override public String getOptionName() { return PLUGIN_NAME; } @Override public String getUsage() { return " " + getArgumentName("") + " Replace collection types with fields having the @XmlElementWrapper and @XmlElement annotations."; } @Override public Collection<QName> getCustomizationElementNames() { return Arrays.asList(XEW_QNAME); } private void initLoggerIfNecessary(Options opts) { if (logger != null) { return; } // Allow the caller to control the log level by explicitly setting this system variable: if (System.getProperty(COMMONS_LOGGING_LOG_LEVEL_PROPERTY_KEY) == null) { String logLevel = "WARN"; if (opts.quiet) { logLevel = "FATAL"; } else if (opts.debugMode) { logLevel = "DEBUG"; } else if (opts.verbose) { logLevel = "INFO"; } System.setProperty(COMMONS_LOGGING_LOG_LEVEL_PROPERTY_KEY, logLevel); } // The logger needs to be re-created and not taken from cache: LogFactory.getFactory().release(); logger = LogFactory.getLog(getClass()); globalConfiguration.setLogger(logger); } protected final void writeSummary(String s) { globalConfiguration.writeSummary(s); } @Override public void onActivated(Options opts) { initLoggerIfNecessary(opts); } /** * Generate argument name from option name. */ private static String getArgumentName(String optionName) { return "-" + PLUGIN_NAME + ":" + optionName; } /** * Parse argument at a given index and apply it to global configuration. Option value may go within the same * argument (separated with equals), or as a following argument. * * @param args * list of arguments * @param index * current index * @param optionName * the option to match * @return number of arguments processed */ private int parseArgument(String[] args, int index, ConfigurationOption option) throws BadCommandLineException { int recognized = 0; String arg = args[index]; String argumentName = getArgumentName(option.optionName()); if (arg.startsWith(argumentName)) { recognized++; try { if (arg.length() > argumentName.length()) { applyConfigurationOption(globalConfiguration, option, arg.substring(argumentName.length()).trim()); } else { applyConfigurationOption(globalConfiguration, option, args[index + 1].trim()); recognized++; } } catch (ClassNotFoundException e) { throw new BadCommandLineException("Invalid class", e); } catch (IOException e) { throw new BadCommandLineException("Failed to read from file", e); } } return recognized; } /** * Parse and apply plugin configuration options. * * @return number of consumed argument options */ @Override public int parseArgument(Options opts, String[] args, int i) throws BadCommandLineException { initLoggerIfNecessary(opts); int recognized = 0; String arg = args[i]; logger.trace("Argument[" + i + "] = " + arg); if (arg.equals(getArgumentName(ConfigurationOption.APPLY_PLURAL_FORM.optionName()))) { globalConfiguration.setApplyPluralForm(true); return 1; } else if ((recognized = parseArgument(args, i, ConfigurationOption.CONTROL)) == 0 && (recognized = parseArgument(args, i, ConfigurationOption.SUMMARY)) == 0 && (recognized = parseArgument(args, i, ConfigurationOption.COLLECTION_INTERFACE)) == 0 // longer option name comes first && (recognized = parseArgument(args, i, ConfigurationOption.COLLECTION_IMPLEMENTATION)) == 0 && (recognized = parseArgument(args, i, ConfigurationOption.INSTANTIATION_MODE)) == 0) { if (arg.startsWith(getArgumentName(""))) { throw new BadCommandLineException("Invalid argument " + arg); } } return recognized; } /** * Try to apply the {@code value} for the given configuration {@code option} to the given {@code configuration}. * Note that depending on configuration level (global, class, property) not every option is applicable (in that case * {@link IllegalArgumentException} is thrown). */ private static void applyConfigurationOption(CommonConfiguration configuration, ConfigurationOption option, String value) throws IOException, ClassNotFoundException { switch (option) { case CONTROL: if (!(configuration instanceof GlobalConfiguration)) { throw new IllegalArgumentException("The option " + option + " is not applicable"); } ((GlobalConfiguration) configuration).readControlFile(value); break; case SUMMARY: if (!(configuration instanceof GlobalConfiguration)) { throw new IllegalArgumentException("The option " + option + " is not applicable"); } ((GlobalConfiguration) configuration).initSummaryWriter(value); break; case COLLECTION_IMPLEMENTATION: configuration.setCollectionImplClass(Class.forName(value)); break; case COLLECTION_INTERFACE: configuration.setCollectionInterfaceClass(Class.forName(value)); break; case INSTANTIATION_MODE: try { configuration.setInstantiationMode(CommonConfiguration.InstantiationMode.valueOf(value.toUpperCase())); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Unknown instantiation mode \"" + value + "\""); } break; case APPLY_PLURAL_FORM: configuration.setApplyPluralForm(Boolean.parseBoolean(value)); break; case ANNOTATE: if (!(configuration instanceof ClassConfiguration)) { throw new IllegalArgumentException("The option " + option + " is not applicable"); } ((ClassConfiguration) configuration).setAnnotatable(Boolean.parseBoolean(value)); break; } } /** * Clone given configuration and apply settings from global/class/field JAXB customization. */ protected static <T extends CommonConfiguration> T applyConfigurationFromCustomizations( CommonConfiguration configuration, CCustomizations customizations, boolean cloneClassConfiguration) throws IOException, ClassNotFoundException { CPluginCustomization customization = customizations.find(XEW_QNAME.getNamespaceURI(), XEW_QNAME.getLocalPart()); if (customization == null) { if (cloneClassConfiguration) { return (T) new ClassConfiguration(configuration); } return (T) configuration; } customization.markAsAcknowledged(); NamedNodeMap attributes = customization.element.getAttributes(); if (cloneClassConfiguration) { configuration = new ClassConfiguration(configuration); } for (int i = 0; i < attributes.getLength(); i++) { Node attribute = attributes.item(i); if (attribute.getNamespaceURI() == null) { applyConfigurationOption(configuration, ConfigurationOption.byOption(attribute.getNodeName()), attribute.getNodeValue()); } } return (T) configuration; } /** * Implements exception handling. */ @Override public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException { try { runInternal(outline); return true; } catch (IOException e) { logger.error("Failed to read the file", e); throw new SAXException(e); } catch (ClassNotFoundException e) { logger.error("Invalid class", e); throw new SAXException(e); } } /** * Actual work is done in this method. */ protected abstract void runInternal(Outline outline) throws ClassNotFoundException, IOException; }