1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 package org.apache.qetest.xslwrapper;
  21 
  22 import java.util.HashMap;
  23 import java.util.Properties;
  24 import javax.xml.transform.Transformer;
  25 import javax.xml.transform.TransformerFactory;
  26 
  27 /**
  28  * A few default implementations of TransformWrapper methods.
  29  *
  30  * A TransformWrapperHelper implements a few of the common methods from
  31  * TransformWrapper that don't directly interact with the underlying processor.
  32  * Individual wrapper implementations are free to extend this class to get some
  33  * free code.
  34  */
  35 public abstract class TransformWrapperHelper implements TransformWrapper {
  36     /**
  37      * TransformerFactory to use; constructed in newProcessor().
  38      */
  39     protected TransformerFactory factory;
  40 
  41     /**
  42      * Cached copy of newProcessor() properties.
  43      */
  44     protected Properties newProcessorOpts;
  45 
  46     /**
  47      * Constant denoting that indent should not be set.
  48      */
  49     private static final int NO_INDENT = -2;
  50 
  51     /**
  52      * Set of stylesheet parameters for use in transforms.
  53      */
  54     protected final HashMap<String,String> m_params = new HashMap<>();
  55 
  56     /**
  57      * If our wrapper has a built stylesheet ready.
  58      */
  59     protected volatile boolean m_stylesheetReady = false;
  60 
  61     /**
  62      * Current number of spaces to indent, default: NO_INDENT. Users call
  63      * setAttribute(ATTRIBUTE_INDENT, int) to set this. If it is set, it will be
  64      * applied to an underlying processor during each transform operation, where
  65      * supported.
  66      */
  67     protected int m_indent = NO_INDENT;
  68 
  69     /**
  70      * Reports if a pre-built/pre-compiled stylesheet is ready; presumably built
  71      * by calling buildStylesheet(xslName).
  72      *
  73      * @return true if one is ready; false otherwise
  74      *
  75      * @see #buildStylesheet(String xslName)
  76      */
  77     @Override
  78     public boolean isStylesheetReady() {
  79         return m_stylesheetReady;
  80     }
  81 
  82     /**
  83      * Set a stylesheet parameter for use in later transforms.
  84      *
  85      * This method merely stores the triple for use later in a transform
  86      * operation. Note that the actual mechanisims for setting parameters in
  87      * implementation differ, especially with regards to namespaces.
  88      *
  89      * Note that the namespace may not contain the "{" or "}" characters, since
  90      * these would be illegal XML namespaces anyways; an
  91      * IllegalArgumentException will be thrown. Note that the name may not begin
  92      * with the "{" character, since it would likely be an illegal XML name
  93      * anyways; an IllegalArgumentException will be thrown.
  94      *
  95      * @param namespace for the parameter
  96      * @param name of the parameter
  97      * @param value of the parameter
  98      *
  99      * @throws IllegalArgumentException thrown if the namespace appears to be
 100      * illegal.
 101      */
 102     @Override
 103     public void setParameter(String namespace, String name, String value)
 104             throws IllegalArgumentException {
 105         if (null != namespace) {
 106             if ((namespace.contains("{"))
 107                     || (namespace.contains("}"))) {
 108                 throw new IllegalArgumentException("setParameter: illegal namespace includes brackets: " + namespace);
 109             }
 110         }
 111         if (null != name) {
 112             if (name.startsWith("{")) {
 113                 throw new IllegalArgumentException(
 114                         "setParameter: illegal name begins with bracket: " + name);
 115             }
 116         }
 117 
 118         if (null != namespace) {
 119             m_params.put("{" + namespace + "}" + name, value);
 120         } else {
 121             m_params.put(name, value);
 122         }
 123     }
 124 
 125     /**
 126      * Get a parameter that was set with setParameter.
 127      *
 128      * Only returns parameters set locally, not parameters exposed by the
 129      * underlying processor implementation. Not terribly useful but I always
 130      * like providing gets for any sets I define.
 131      *
 132      * @param namespace for the parameter
 133      * @param name of the parameter
 134      */
 135     @Override
 136     public Object getParameter(String namespace, String name) {
 137         if (null == m_params) {
 138             return null;
 139         }
 140 
 141         if (null != namespace) {
 142             return m_params.get("{" + namespace + "}" + name);
 143         } else {
 144             return m_params.get(name);
 145         }
 146     }
 147 
 148     /**
 149      * Apply the parameters that were set with setParameter to our underlying
 150      * processor implementation.
 151      *
 152      * Subclasses may call this to apply all set parameters during each
 153      * transform if they override the applyParameter() method to set a single
 154      * parameter.
 155      *
 156      * This is a convenience method for getting data out of m_params that was
 157      * encoded by our setParameter().
 158      *
 159      * @param transformer a Transformer object.
 160      */
 161     protected void applyParameters(Transformer transformer) {
 162         m_params.forEach((key, value) -> {
 163             String namespace = null;
 164             String name;
 165             if (key.startsWith("{")) {
 166                 int idx = key.indexOf("}");
 167                 namespace = key.substring(1, idx);
 168                 name = key.substring(idx + 1);
 169             } else {
 170                 // namespace stays null
 171                 name = key;
 172             }
 173             // Call subclassed worker method for each parameter
 174             applyParameter(transformer, namespace, name, value);
 175         });
 176     }
 177 
 178     /**
 179      * Apply a single parameter to our underlying processor implementation: must
 180      * be overridden.
 181      *
 182      * Subclasses must override; this class will throw an IllegalStateException
 183      * since we can't do anything.
 184      *
 185      * @param transformer a Transformer object.
 186      * @param namespace for the parameter, may be null
 187      * @param name for the parameter, should not be null
 188      * @param value for the parameter, may be null
 189      */
 190     protected abstract void applyParameter(Transformer transformer, String namespace,
 191             String name, String value);
 192 
 193     /**
 194      * Reset our parameters and wrapper state, and optionally force creation of
 195      * a new underlying processor implementation.
 196      *
 197      * This class clears the indent and any parameters. Subclasses are free to
 198      * call us to get this default behavior or not. Note that subclasses must
 199      * clear m_stylesheetReady themselves if needed.
 200      *
 201      * @param newProcessor ignored in this class
 202      */
 203     @Override
 204     public void reset(boolean newProcessor) {
 205         m_params.clear();
 206         m_indent = NO_INDENT;
 207     }
 208 
 209     /**
 210      * Ensure newProcessor has been called when needed.
 211      *
 212      * Prevent users from shooting themselves in the foot by calling a
 213      * transform* API before newProcessor().
 214      *
 215      * @throws java.lang.Exception
 216      */
 217     public void preventFootShooting() throws Exception {
 218         if (null == factory) {
 219             newProcessor(newProcessorOpts);
 220         }
 221     }
 222 }