Coverage Report - org.apache.commons.flatfile.dsl.ParserEntityFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
ParserEntityFactory
74%
62/83
39%
11/28
1.923
ParserEntityFactory$1
100%
5/5
N/A
1.923
ParserEntityFactory$2
88%
8/9
50%
2/4
1.923
ParserEntityFactory$EntityHolder
100%
10/10
100%
2/2
1.923
ParserEntityFactory$EntityHolder$1
100%
3/3
N/A
1.923
 
 1  426
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.flatfile.dsl;
 18  
 
 19  
 import java.io.InputStream;
 20  
 import java.util.ArrayList;
 21  
 import java.util.HashMap;
 22  
 import java.util.Map;
 23  
 
 24  
 import org.apache.commons.collections4.Transformer;
 25  
 import org.apache.commons.flatfile.DynamicField;
 26  
 import org.apache.commons.flatfile.Entity;
 27  
 import org.apache.commons.flatfile.EntityFactory;
 28  
 import org.apache.commons.flatfile.Field;
 29  
 import org.apache.commons.flatfile.util.ApplyOptions;
 30  
 import org.apache.commons.lang3.ObjectUtils;
 31  
 import org.apache.commons.logging.Log;
 32  
 import org.apache.commons.logging.LogFactory;
 33  
 import org.apache.commons.pool.BasePoolableObjectFactory;
 34  
 import org.apache.commons.pool.ObjectPool;
 35  
 import org.apache.commons.pool.impl.StackObjectPool;
 36  
 
 37  
 import antlr.TokenBuffer;
 38  
 import antlr.collections.AST;
 39  
 
 40  
 /**
 41  
  * Entity factory; provides access to named entities parsed from one or more description files.
 42  
  * @version $Revision: 1766123 $ $Date: 2016-10-21 15:40:45 -0500 (Fri, 21 Oct 2016) $
 43  
  */
 44  213
 public class ParserEntityFactory implements EntityFactory {
 45  
 
 46  
     /** Field option constant */
 47  
     public static final String OPTION_FIELD = "field";
 48  
     /** DynamicField option constant */
 49  
     public static final String OPTION_DYNAMIC_FIELD = "dynamicField";
 50  
 
 51  6
     private static final Log LOG = LogFactory.getLog(ParserEntityFactory.class);
 52  6
     private static final EntityNameStrategy DEFAULT_NAME_STRATEGY = new DefaultEntityNameStrategy();
 53  
 
 54  213
     private class EntityHolder {
 55  
 
 56  213
         private final EntityDefinition definition;
 57  
         private Entity prototype;
 58  
 
 59  
         /**
 60  
          * Create a new EntityHolder instance.
 61  
          * @param definition base
 62  
          */
 63  1638
         EntityHolder(EntityDefinition definition) {
 64  1638
             this.definition = definition;
 65  1638
         }
 66  
 
 67  
         /**
 68  
          * Get the prototypical entity from this {@link EntityHolder}.
 69  
          * @return Entity
 70  
          */
 71  
         Entity getPrototype() {
 72  786
             synchronized (this) {
 73  528
                 if (prototype == null) {
 74  639
                     prototype = doWithPooledTreeParser(new Transformer<EntityTreeParser, Entity>() {
 75  
 
 76  
                         public Entity transform(EntityTreeParser input) {
 77  426
                             return input.createEntity(definition);
 78  
                         }
 79  
                     });
 80  
                 }
 81  258
             }
 82  516
             return prototype;
 83  
         }
 84  
     }
 85  
 
 86  
     private EntityNameStrategy entityNameStrategy;
 87  
     private Map<String, EntityHolder> entityMap;
 88  192
     private final Map<String, Map<String, ?>> defaultOptionMaps = new HashMap<String, Map<String, ?>>();
 89  
     private boolean checked;
 90  
     private InputStream[] sources;
 91  
     private EntityFactory parent;
 92  
 
 93  192
     private final ObjectPool<EntityTreeParser> treeParserPool =
 94  192
         new StackObjectPool<EntityTreeParser>(new BasePoolableObjectFactory<EntityTreeParser>() {
 95  
 
 96  
             @Override
 97  
             public EntityTreeParser makeObject() throws Exception {
 98  222
                 EntityTreeParser result = new EntityTreeParser();
 99  222
                 result.setEntityFactory(ParserEntityFactory.this);
 100  222
                 return result;
 101  
             }
 102  96
         }, 1);
 103  
 
 104  
     /**
 105  
      * Create a new ParserEntityFactory.
 106  
      */
 107  0
     public ParserEntityFactory() {
 108  0
     }
 109  
 
 110  
     /**
 111  
      * Create a new ParserEntityFactory.
 112  
      * @param source to read
 113  
      */
 114  192
     public ParserEntityFactory(InputStream source) {
 115  192
         setSource(source);
 116  192
     }
 117  
 
 118  
     /**
 119  
      * Create a new ParserEntityFactory.
 120  
      * @param sources to read
 121  
      */
 122  0
     public ParserEntityFactory(InputStream[] sources) {
 123  0
         setSources(sources);
 124  0
     }
 125  
 
 126  
     /**
 127  
      * {@inheritDoc}
 128  
      */
 129  
     public final Entity getEntity(Object cue) {
 130  528
         EntityHolder holder = getEntityMap().get(getEntityNameStrategy().getEntityName(cue));
 131  528
         if (holder != null) {
 132  528
             return holder.getPrototype().clone();
 133  
         }
 134  0
         return getParent() == null ? null : getParent().getEntity(cue);
 135  
     }
 136  
 
 137  
     /**
 138  
      * Validate all defined entities.
 139  
      */
 140  
     public final synchronized void validate() {
 141  0
         if (checked) {
 142  0
             return;
 143  
         }
 144  0
         for (String name : getEntityMap().keySet()) {
 145  0
             getEntity(name);
 146  0
         }
 147  0
     }
 148  
 
 149  
     /**
 150  
      * Get the entityNameStrategy.
 151  
      * @return EntityNameStrategy.
 152  
      */
 153  
     public synchronized EntityNameStrategy getEntityNameStrategy() {
 154  528
         return entityNameStrategy == null ? DEFAULT_NAME_STRATEGY : entityNameStrategy;
 155  
     }
 156  
 
 157  
     /**
 158  
      * Set the entityNameStrategy.
 159  
      * @param entityNameStrategy The EntityNameStrategy entityNameStrategy to set.
 160  
      */
 161  
     public synchronized void setEntityNameStrategy(EntityNameStrategy entityNameStrategy) {
 162  12
         this.entityNameStrategy = entityNameStrategy;
 163  12
     }
 164  
 
 165  
     /**
 166  
      * Get the InputStream[] sources.
 167  
      * @return InputStream[]
 168  
      */
 169  
     public InputStream[] getSources() {
 170  192
         return sources;
 171  
     }
 172  
 
 173  
     /**
 174  
      * Set the InputStream[] sources.
 175  
      * @param sources InputStream[]
 176  
      */
 177  
     public void setSources(InputStream[] sources) {
 178  192
         this.sources = sources;
 179  192
     }
 180  
 
 181  
     /**
 182  
      * Convenience setter for a single InputStream source.
 183  
      * @param source to set
 184  
      */
 185  
     public void setSource(InputStream source) {
 186  192
         setSources(new InputStream[] { source });
 187  192
     }
 188  
 
 189  
     /**
 190  
      * Get the parent.
 191  
      * @return EntityFactory
 192  
      */
 193  
     public EntityFactory getParent() {
 194  0
         return parent;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Set the parent.
 199  
      * @param parent EntityFactory
 200  
      */
 201  
     public void setParent(EntityFactory parent) {
 202  0
         this.parent = parent;
 203  0
     }
 204  
 
 205  
     /**
 206  
      * Add an EntityDefinition
 207  
      * @param name to associate
 208  
      * @param def to add
 209  
      */
 210  
     void add(String name, EntityDefinition def) {
 211  1638
         entityMap.put(name, new EntityHolder(def));
 212  1638
     }
 213  
 
 214  
     /**
 215  
      * Set the default options for a given entity "type".
 216  
      * @param type String key
 217  
      * @param options option Map
 218  
      */
 219  
     void setDefaultOptions(String type, Map<String, ? extends Object> options) {
 220  18
         Map<String, ? extends Object> old = defaultOptionMaps.put(type, options);
 221  18
         if (!ObjectUtils.equals(old, options)) {
 222  18
             LOG.warn("Overriding " + type + " options");
 223  
         }
 224  18
     }
 225  
 
 226  
     /**
 227  
      * Get the default option map for a given entity "type".
 228  
      * @param type String key
 229  
      * @return option Map
 230  
      */
 231  
     Map<String, ? extends Object> getDefaultOptions(String type) {
 232  0
         return defaultOptionMaps.get(type);
 233  
     }
 234  
 
 235  
     /**
 236  
      * Create a Field of a particular length, applying default options.
 237  
      * @param length of field
 238  
      * @return Field
 239  
      */
 240  
     Field createField(int length) {
 241  456
         return applyDefaultOptions(new Field(length), OPTION_FIELD);
 242  
     }
 243  
 
 244  
     /**
 245  
      * Create a dynamic field, applying default options.
 246  
      * @return DynamicField
 247  
      */
 248  
     DynamicField createDynamicField() {
 249  84
         return applyDefaultOptions(new DynamicField(), OPTION_DYNAMIC_FIELD);
 250  
     }
 251  
 
 252  
     /**
 253  
      * Create a field of a particular value, applying default options.
 254  
      * @param value of the field.
 255  
      * @return Field
 256  
      */
 257  
     Field createField(byte[] value) {
 258  228
         return applyDefaultOptions(new Field(value), OPTION_FIELD);
 259  
     }
 260  
 
 261  
     /**
 262  
      * Apply the default options of the specified type to the specified Entity.
 263  
      * @param <E> Entity subtype
 264  
      * @param e E
 265  
      * @param type associated field type
 266  
      * @return e
 267  
      */
 268  
     private <E extends Entity> E applyDefaultOptions(E e, String type) {
 269  768
         Map<String, ? extends Object> m = defaultOptionMaps.get(type);
 270  768
         return m == null ? e : ApplyOptions.apply(e, m);
 271  
     }
 272  
 
 273  
     /**
 274  
      * Return our entity map, initializing if necessary.
 275  
      * @return Map<String, EntityProxy> - value may be an EntityDefinition or an Entity.
 276  
      */
 277  
     private synchronized Map<String, EntityHolder> getEntityMap() {
 278  528
         if (entityMap == null) {
 279  192
             entityMap = new HashMap<String, EntityHolder>();
 280  
             try {
 281  192
                 EntityParser p = null;
 282  192
                 final ArrayList<AST> trees = new ArrayList<AST>();
 283  192
                 InputStream[] is = getSources();
 284  384
                 for (InputStream element : is) {
 285  192
                     TokenBuffer tb = new TokenBuffer(new EntityLexer(element));
 286  192
                     if (p == null) {
 287  192
                         p = new EntityParser(tb);
 288  96
                     } else {
 289  0
                         p.setTokenBuffer(tb);
 290  
                     }
 291  192
                     p.parse();
 292  192
                     trees.add(p.getAST());
 293  
                 }
 294  192
                 doWithPooledTreeParser(new Transformer<EntityTreeParser, Void>() {
 295  
 
 296  
                     public Void transform(EntityTreeParser input) {
 297  384
                         for (AST ast : trees) {
 298  
                             try {
 299  192
                                 input.load(ast);
 300  96
                             } catch (Exception e) {
 301  0
                                 throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
 302  96
                             }
 303  96
                         }
 304  192
                         return null;
 305  
                     }
 306  
                 });
 307  96
             } catch (Exception e) {
 308  0
                 throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
 309  96
             }
 310  
         }
 311  528
         return entityMap;
 312  
     }
 313  
 
 314  
     /**
 315  
      * Perform some function against a borrowed {@link EntityTreeParser} instance from the pool.
 316  
      * @param xform function
 317  206
      * @param <T> expected result type of {@code xform}
 318  0
      * @return result from invoking {@code xform.transform(entityTreeParser)}
 319  71
      */
 320  348
     private <T> T doWithPooledTreeParser(Transformer<EntityTreeParser, T> xform) {
 321  206
         EntityTreeParser entityTreeParser;
 322  103
         try {
 323  614
             entityTreeParser = treeParserPool.borrowObject();
 324  206
         } catch (Exception e) {
 325  0
             throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
 326  408
         }
 327  511
         T result = xform.transform(entityTreeParser);
 328  101
         try {
 329  404
             treeParserPool.returnObject(entityTreeParser);
 330  202
         } catch (Exception e) {
 331  0
             LOG.error("Error returning EntityTreeParser to pool", e);
 332  202
         }
 333  303
         return result;
 334  
     }
 335  
 }