/* * Copyright 2013-2016 Skynav, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.skynav.ttx.app; import java.io.PrintWriter; import java.net.URI; import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import org.w3c.dom.Node; import com.skynav.ttv.app.InvalidOptionUsageException; import com.skynav.ttv.app.MissingOptionArgumentException; import com.skynav.ttv.app.OptionSpecification; import com.skynav.ttv.app.RestartOptions; import com.skynav.ttv.app.ResultProcessor; import com.skynav.ttv.app.TimedTextVerifier; import com.skynav.ttv.app.UnknownOptionException; import com.skynav.ttv.model.Model; import com.skynav.ttv.util.Condition; import com.skynav.ttv.util.ExternalParameters; import com.skynav.ttv.util.Reporter; import com.skynav.ttx.transformer.Transformer; import com.skynav.ttx.transformer.TransformerContext; import com.skynav.ttx.transformer.TransformerOptions; import com.skynav.ttx.transformer.Transformers; public class TimedTextTransformer implements ResultProcessor, TransformerContext { // banner text private static final String title = "Timed Text Transformer (TTX) [" + Version.CURRENT + "]"; private static final String copyright = "Copyright 2013-16 Skynav, Inc."; private static final String banner = title + " " + copyright; // option and usage info private static final String[][] shortOptionSpecifications = new String[][] { }; private static final Collection<OptionSpecification> shortOptions; static { Set<OptionSpecification> s = new java.util.TreeSet<OptionSpecification>(); for (String[] spec : shortOptionSpecifications) { s.add(new OptionSpecification(spec[0], spec[1])); } shortOptions = Collections.unmodifiableSet(s); } private static final String[][] longOptionSpecifications = new String[][] { { "show-memory", "", "show memory statistics" }, { "show-transformers", "", "show built-in transformers (use with --verbose to show more details)" }, { "transformer", "NAME", "specify transformer name (default: " + Transformers.getDefaultTransformerName() + ")" }, }; private static final Collection<OptionSpecification> longOptions; static { Set<OptionSpecification> s = new java.util.TreeSet<OptionSpecification>(); for (String[] spec : longOptionSpecifications) { s.add(new OptionSpecification(spec[0], spec[1], spec[2])); } longOptions = Collections.unmodifiableSet(s); } private static final String usageCommand = "java -jar ttx.jar [options] URL*"; private static final String[][] nonOptions = new String[][] { { "URL", "an absolute or relative URL; if relative, resolved against current working directory" }, }; // controlling verifier private TimedTextVerifier verifier; // options state private boolean showMemory; private boolean showTransformers; // pre-processed options state private List<TransformerOptions> transformerOptions; private Map<String,OptionSpecification> mergedShortOptionsMap; private Map<String,OptionSpecification> mergedLongOptionsMap; // derived option state private Transformer transformer; public TimedTextTransformer() { } public void resetAllState(boolean restart) { resetResourceState(restart); resetOptionsState(restart); resetGlobalState(restart); } private void resetResourceState(boolean restart) { } private void resetOptionsState(boolean restart) { showMemory = false; showTransformers = false; transformerOptions = null; mergedShortOptionsMap = null; mergedLongOptionsMap = null; if (transformer != null) transformer.resetAllState(restart); transformer = null; } private void resetGlobalState(boolean restart) { if (!restart) { verifier = null; } } protected boolean showMemory() { return showMemory; } protected void setShowMemory(boolean showMemory) { this.showMemory = showMemory; } protected long getUsedMemory() { return getUsedMemory(true); } protected long getUsedMemory(boolean gc) { Runtime r = Runtime.getRuntime(); if (gc) r.gc(); return r.totalMemory() - r.freeMemory(); } protected void setReporter(Reporter reporter, PrintWriter reporterOutput, String reporterOutputEncoding, boolean reporterIncludeSource, boolean closeOldReporter) { verifier.setReporter(reporter, reporterOutput, reporterOutputEncoding, reporterIncludeSource, closeOldReporter); } @Override public void processResult(List<String> args, URI uri, Object root) { Reporter reporter = getReporter(); initializeResourceState(uri, getModel()); if (transformer != null) { long preTransformMemory = 0; long postTransformMemory = 0; if (showMemory) { preTransformMemory = getUsedMemory(); reporter.logInfo(reporter.message("*KEY*", "Pre-transform memory usage: {0}", preTransformMemory)); } transformer.transform(args, root, null); if (showMemory) { postTransformMemory = getUsedMemory(); reporter.logInfo(reporter.message("*KEY*", "Post-transform memory usage: {0}, delta: {1}", postTransformMemory, postTransformMemory - preTransformMemory)); } } } protected void initializeResourceState(URI uri, Model model) { this .initializeResourceState(uri); model .initializeResourceState(uri, verifier.getResourceState()); } protected void initializeResourceState(URI uri) { setResourceState(TransformerContext.ResourceState.ttxConditionEvaluatorState.name(), makeConditionEvaluatorState()); setResourceState(TransformerContext.ResourceState.ttxDontElideInitials.name(), Boolean.FALSE); setResourceState(TransformerContext.ResourceState.ttxInputUri.name(), uri); setResourceState(TransformerContext.ResourceState.ttxOutput.name(), null); setResourceState(TransformerContext.ResourceState.ttxRetainLocations.name(), Boolean.FALSE); setResourceState(TransformerContext.ResourceState.ttxRetainMetadata.name(), Boolean.FALSE); setResourceState(TransformerContext.ResourceState.ttxSuppressOutputSerialization.name(), Boolean.FALSE); setResourceState(TransformerContext.ResourceState.ttxTransformer.name(), transformer); } protected Condition.EvaluatorState makeConditionEvaluatorState() { return Condition.makeEvaluatorState(getConditionMediaParameters(), getConditionBoundParameters(), getConditionSupportedFeatures()); } protected Map<String,Object> getConditionBoundParameters() { Map<String,Object> parameters = new java.util.HashMap<String,Object>(); parameters.put("forced", Boolean.FALSE); parameters.put("mediaAspectRatio", null); parameters.put("mediaLanguage", null); parameters.put("userLanguage", Locale.getDefault().getLanguage()); return parameters; } protected Map<String,Object> getConditionMediaParameters() { return new java.util.HashMap<String,Object>(); } protected Set<String> getConditionSupportedFeatures() { return new java.util.HashSet<String>(); } // TransformerContext implementation public ExternalParameters getExternalParameters() { return verifier.getExternalParameters(); } public Reporter getReporter() { return verifier.getReporter(); } public Model getModel() { return verifier.getModel(); } public QName getBindingElementName(Object value) { return verifier.getBindingElementName(value); } public Object getBindingElementParent(Object value) { return verifier.getBindingElementParent(value); } public Object getBindingElement(Node node) { return verifier.getBindingElement(node); } public Node getXMLNode(Object value) { return verifier.getXMLNode(value); } public void setResourceState(String key, Object value) { verifier.setResourceState(key, value); } public Object getResourceState(String key) { return verifier.getResourceState(key); } public Object extractResourceState(String key) { return verifier.extractResourceState(key); } public Condition.EvaluatorState getConditionEvaluatorState() { return (Condition.EvaluatorState) getResourceState(ResourceState.ttxConditionEvaluatorState.name()); } public URL getDefaultConfigurationLocator() { return Configuration.getDefaultConfigurationLocator(); } public com.skynav.ttv.util.ConfigurationDefaults getConfigurationDefaults(URL locator) { return new ConfigurationDefaults(locator); } public Class<? extends com.skynav.ttv.util.Configuration> getConfigurationClass() { return Configuration.class; } public List<String> preProcessOptions(List<String> args, com.skynav.ttv.util.Configuration configuration, Collection<OptionSpecification> baseShortOptions, Collection<OptionSpecification> baseLongOptions) { String transformerName = null; // extract transformer name from configuration options if present if (configuration != null) { for (Map.Entry<String,String> e : configuration.getOptions().entrySet()) { String n = e.getKey(); String v = e.getValue(); if (n.equals("transformer")) transformerName = v; } } // extract transformer name from argument options if present List<String> skippedArgs = new java.util.ArrayList<String>(); for (int i = 0, n = args.size(); i < n; ++i) { String arg = args.get(i); if (arg.indexOf("--") == 0) { String option = arg.substring(2); if (option.equals("transformer")) { if (i + 1 >= n) throw new MissingOptionArgumentException("--" + option); transformerName = args.get(++i); } else { skippedArgs.add(arg); } } else skippedArgs.add(arg); } // derive transformer Transformer transformer; if (transformerName != null) { transformer = Transformers.getTransformer(transformerName, this); if (transformer == null) throw new InvalidOptionUsageException("transformer", "unknown transformer: " + transformerName); } else { transformer = Transformers.getDefaultTransformer(this); } this.transformer = transformer; populateMergedOptionMaps(baseShortOptions, baseLongOptions); return skippedArgs; } protected boolean doMergeTransformerOptions() { return true; } private void populateMergedOptionMaps(Collection<OptionSpecification> baseShortOptions, Collection<OptionSpecification> baseLongOptions) { // configure for transformer options merging List<TransformerOptions> transformerOptions; Collection<OptionSpecification> shortOptions; Collection<OptionSpecification> longOptions; if (doMergeTransformerOptions()) { transformerOptions = new java.util.ArrayList<TransformerOptions>(Arrays.asList(new TransformerOptions[] { transformer })); shortOptions = TimedTextTransformer.shortOptions; longOptions = TimedTextTransformer.longOptions; } else { transformerOptions = null; shortOptions = null; longOptions = null; } populateMergedOptionsMaps(baseShortOptions, baseLongOptions, transformerOptions, shortOptions, longOptions); } protected void populateMergedOptionsMaps(Collection<OptionSpecification> baseShortOptions, Collection<OptionSpecification> baseLongOptions, List<TransformerOptions> transformerOptions, Collection<OptionSpecification> shortOptions, Collection<OptionSpecification> longOptions) { // short options Collection<OptionSpecification> mergedShortOptions = mergeOptions(baseShortOptions, shortOptions); if (transformerOptions != null) { for (TransformerOptions options : transformerOptions) { mergedShortOptions = mergeOptions(mergedShortOptions, options.getShortOptionSpecs()); } } Map<String,OptionSpecification> mergedShortOptionsMap = new java.util.TreeMap<String,OptionSpecification>(); if ((this.mergedShortOptionsMap != null) && !this.mergedShortOptionsMap.isEmpty()) mergedShortOptionsMap.putAll(this.mergedShortOptionsMap); for (OptionSpecification s : mergedShortOptions) mergedShortOptionsMap.put(s.getName(), s); this.mergedShortOptionsMap = mergedShortOptionsMap; // long options Collection<OptionSpecification> mergedLongOptions = mergeOptions(baseLongOptions, longOptions); if (transformerOptions != null) { for (TransformerOptions options : transformerOptions) { mergedLongOptions = mergeOptions(mergedLongOptions, options.getLongOptionSpecs()); } } Map<String,OptionSpecification> mergedLongOptionsMap = new java.util.TreeMap<String,OptionSpecification>(); if ((this.mergedLongOptionsMap != null) && !this.mergedLongOptionsMap.isEmpty()) mergedLongOptionsMap.putAll(this.mergedLongOptionsMap); for (OptionSpecification s : mergedLongOptions) mergedLongOptionsMap.put(s.getName(), s); this.mergedLongOptionsMap = mergedLongOptionsMap; // update transformer options List<TransformerOptions> mergedTransformerOptions = new java.util.ArrayList<TransformerOptions>(); if ((this.transformerOptions != null) && !this.transformerOptions.isEmpty()) mergedTransformerOptions.addAll(this.transformerOptions); if ((transformerOptions != null) && !transformerOptions.isEmpty()) mergedTransformerOptions.addAll(transformerOptions); this.transformerOptions = mergedTransformerOptions; } protected Collection<OptionSpecification> mergeOptions(Collection<OptionSpecification> options, Collection<OptionSpecification> additionalOptions) { Collection<OptionSpecification> mergedOptions = new java.util.TreeSet<OptionSpecification>(); if (options != null) { for (OptionSpecification os : options) mergedOptions.add(os); } if (additionalOptions != null) { for (OptionSpecification os : additionalOptions) mergedOptions.add(os); } return mergedOptions; } public boolean hasOption(String arg) { assert arg.length() >= 2; assert arg.charAt(0) == '-'; if (arg.charAt(1) == '-') return hasLongOption(arg.substring(2)); else return hasShortOption(arg.substring(1)); } public int parseOption(List<String> args, int index) { String option = args.get(index); assert option.length() >= 2; assert option.charAt(0) == '-'; if (option.charAt(1) == '-') return parseLongOption(args, index); else return parseShortOption(args, index); } public void processDerivedOptions() { /* Transformer transformer; if (transformerName != null) { transformer = Transformers.getTransformer(transformerName); if (transformer == null) throw new InvalidOptionUsageException("transformer", "unknown transformer: " + transformerName); } else if (getResourceState(TransformerContext.ResourceState.ttxTransformer.name()) != null) { transformer = (Transformer) getResourceState(TransformerContext.ResourceState.ttxTransformer.name()); } else transformer = Transformers.getDefaultTransformer(); this.transformer = transformer; */ assert transformer != null; transformer.processDerivedOptions(); } public List<String> processNonOptionArguments(List<String> nonOptionArgs) { return nonOptionArgs; } public List<String> processRestartArguments(List<String> args, List<String> nonOptionArgs, RestartOptions restartOptions) { return args; } public void showBanner(PrintWriter out) { showBanner(out, TimedTextTransformer.banner); } protected void showBanner(PrintWriter out, String banner) { verifier.showBanner(out, banner); } public void showUsage(PrintWriter out) { out.print("Usage: " + getShowUsageCommand() + "\n"); TimedTextVerifier.showOptions(out, "Short Options", mergedShortOptionsMap.values()); TimedTextVerifier.showOptions(out, "Long Options", mergedLongOptionsMap.values()); TimedTextVerifier.showOptions(out, "Non-Option Arguments", nonOptions); } public String getShowUsageCommand() { return usageCommand; } public void runOptions(PrintWriter out) { if (showTransformers) showTransformers(out); } protected boolean hasLongOption(String option) { return mergedLongOptionsMap.containsKey(option); } protected int parseLongOption(List<String> args, int index) { String arg = args.get(index); int numArgs = args.size(); String option = arg; assert option.length() > 2; option = option.substring(2); if (option.equals("show-memory")) { showMemory = true; } else if (option.equals("show-transformers")) { showTransformers = true; } else if (option.equals("transformer")) { if (index + 1 > numArgs) throw new MissingOptionArgumentException("--" + option); ++index; // ignore - already processed by #preProcessOptions } else { if (transformerOptions != null) { for (TransformerOptions options: transformerOptions) { int i = options.parseLongOption(args, index); if (i > index) return i; } } throw new UnknownOptionException("--" + option); } return index + 1; } protected boolean hasShortOption(String option) { return mergedShortOptionsMap.containsKey(option); } protected int parseShortOption(List<String> args, int index) { String arg = args.get(index); String option = arg; assert option.length() == 2; option = option.substring(1); if (transformerOptions != null) { for (TransformerOptions options: transformerOptions) { int i = options.parseShortOption(args, index); if (i > index) return i; } } throw new UnknownOptionException("--" + option); } private void showTransformers(PrintWriter out) { String defaultTransformerName = Transformers.getDefaultTransformerName(); StringBuffer sb = new StringBuffer(); sb.append("Transformers:\n"); for (String transformerName : Transformers.getTransformerNames()) { sb.append(" "); sb.append(transformerName); if (transformerName.equals(defaultTransformerName)) { sb.append(" (default)"); } sb.append('\n'); } out.println(sb.toString()); } public int run(String[] args) { return run(Arrays.asList(args)); } public int run(List<String> args) { return (verifier = new TimedTextVerifier()).run(args, this); } public TimedTextVerifier.Results getResults(String uri) { return verifier.getResults(uri); } public int getResultCode(String uri) { return verifier.getResultCode(uri); } public int getResultFlags(String uri) { return verifier.getResultFlags(uri); } public static void main(String[] args) { Runtime.getRuntime().exit(new TimedTextTransformer().run(args)); } }