001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.flatfile.dsl; 018 019import java.io.InputStream; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.Map; 023 024import org.apache.commons.collections4.Transformer; 025import org.apache.commons.flatfile.DynamicField; 026import org.apache.commons.flatfile.Entity; 027import org.apache.commons.flatfile.EntityFactory; 028import org.apache.commons.flatfile.Field; 029import org.apache.commons.flatfile.util.ApplyOptions; 030import org.apache.commons.lang3.ObjectUtils; 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.apache.commons.pool.BasePoolableObjectFactory; 034import org.apache.commons.pool.ObjectPool; 035import org.apache.commons.pool.impl.StackObjectPool; 036 037import antlr.TokenBuffer; 038import antlr.collections.AST; 039 040/** 041 * Entity factory; provides access to named entities parsed from one or more description files. 042 * @version $Revision: 1766123 $ $Date: 2016-10-21 15:40:45 -0500 (Fri, 21 Oct 2016) $ 043 */ 044public class ParserEntityFactory implements EntityFactory { 045 046 /** Field option constant */ 047 public static final String OPTION_FIELD = "field"; 048 /** DynamicField option constant */ 049 public static final String OPTION_DYNAMIC_FIELD = "dynamicField"; 050 051 private static final Log LOG = LogFactory.getLog(ParserEntityFactory.class); 052 private static final EntityNameStrategy DEFAULT_NAME_STRATEGY = new DefaultEntityNameStrategy(); 053 054 private class EntityHolder { 055 056 private final EntityDefinition definition; 057 private Entity prototype; 058 059 /** 060 * Create a new EntityHolder instance. 061 * @param definition base 062 */ 063 EntityHolder(EntityDefinition definition) { 064 this.definition = definition; 065 } 066 067 /** 068 * Get the prototypical entity from this {@link EntityHolder}. 069 * @return Entity 070 */ 071 Entity getPrototype() { 072 synchronized (this) { 073 if (prototype == null) { 074 prototype = doWithPooledTreeParser(new Transformer<EntityTreeParser, Entity>() { 075 076 public Entity transform(EntityTreeParser input) { 077 return input.createEntity(definition); 078 } 079 }); 080 } 081 } 082 return prototype; 083 } 084 } 085 086 private EntityNameStrategy entityNameStrategy; 087 private Map<String, EntityHolder> entityMap; 088 private final Map<String, Map<String, ?>> defaultOptionMaps = new HashMap<String, Map<String, ?>>(); 089 private boolean checked; 090 private InputStream[] sources; 091 private EntityFactory parent; 092 093 private final ObjectPool<EntityTreeParser> treeParserPool = 094 new StackObjectPool<EntityTreeParser>(new BasePoolableObjectFactory<EntityTreeParser>() { 095 096 @Override 097 public EntityTreeParser makeObject() throws Exception { 098 EntityTreeParser result = new EntityTreeParser(); 099 result.setEntityFactory(ParserEntityFactory.this); 100 return result; 101 } 102 }, 1); 103 104 /** 105 * Create a new ParserEntityFactory. 106 */ 107 public ParserEntityFactory() { 108 } 109 110 /** 111 * Create a new ParserEntityFactory. 112 * @param source to read 113 */ 114 public ParserEntityFactory(InputStream source) { 115 setSource(source); 116 } 117 118 /** 119 * Create a new ParserEntityFactory. 120 * @param sources to read 121 */ 122 public ParserEntityFactory(InputStream[] sources) { 123 setSources(sources); 124 } 125 126 /** 127 * {@inheritDoc} 128 */ 129 public final Entity getEntity(Object cue) { 130 EntityHolder holder = getEntityMap().get(getEntityNameStrategy().getEntityName(cue)); 131 if (holder != null) { 132 return holder.getPrototype().clone(); 133 } 134 return getParent() == null ? null : getParent().getEntity(cue); 135 } 136 137 /** 138 * Validate all defined entities. 139 */ 140 public final synchronized void validate() { 141 if (checked) { 142 return; 143 } 144 for (String name : getEntityMap().keySet()) { 145 getEntity(name); 146 } 147 } 148 149 /** 150 * Get the entityNameStrategy. 151 * @return EntityNameStrategy. 152 */ 153 public synchronized EntityNameStrategy getEntityNameStrategy() { 154 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 this.entityNameStrategy = entityNameStrategy; 163 } 164 165 /** 166 * Get the InputStream[] sources. 167 * @return InputStream[] 168 */ 169 public InputStream[] getSources() { 170 return sources; 171 } 172 173 /** 174 * Set the InputStream[] sources. 175 * @param sources InputStream[] 176 */ 177 public void setSources(InputStream[] sources) { 178 this.sources = sources; 179 } 180 181 /** 182 * Convenience setter for a single InputStream source. 183 * @param source to set 184 */ 185 public void setSource(InputStream source) { 186 setSources(new InputStream[] { source }); 187 } 188 189 /** 190 * Get the parent. 191 * @return EntityFactory 192 */ 193 public EntityFactory getParent() { 194 return parent; 195 } 196 197 /** 198 * Set the parent. 199 * @param parent EntityFactory 200 */ 201 public void setParent(EntityFactory parent) { 202 this.parent = parent; 203 } 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 entityMap.put(name, new EntityHolder(def)); 212 } 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 Map<String, ? extends Object> old = defaultOptionMaps.put(type, options); 221 if (!ObjectUtils.equals(old, options)) { 222 LOG.warn("Overriding " + type + " options"); 223 } 224 } 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 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 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 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 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 Map<String, ? extends Object> m = defaultOptionMaps.get(type); 270 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 if (entityMap == null) { 279 entityMap = new HashMap<String, EntityHolder>(); 280 try { 281 EntityParser p = null; 282 final ArrayList<AST> trees = new ArrayList<AST>(); 283 InputStream[] is = getSources(); 284 for (InputStream element : is) { 285 TokenBuffer tb = new TokenBuffer(new EntityLexer(element)); 286 if (p == null) { 287 p = new EntityParser(tb); 288 } else { 289 p.setTokenBuffer(tb); 290 } 291 p.parse(); 292 trees.add(p.getAST()); 293 } 294 doWithPooledTreeParser(new Transformer<EntityTreeParser, Void>() { 295 296 public Void transform(EntityTreeParser input) { 297 for (AST ast : trees) { 298 try { 299 input.load(ast); 300 } catch (Exception e) { 301 throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); 302 } 303 } 304 return null; 305 } 306 }); 307 } catch (Exception e) { 308 throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); 309 } 310 } 311 return entityMap; 312 } 313 314 /** 315 * Perform some function against a borrowed {@link EntityTreeParser} instance from the pool. 316 * @param xform function 317 * @param <T> expected result type of {@code xform} 318 * @return result from invoking {@code xform.transform(entityTreeParser)} 319 */ 320 private <T> T doWithPooledTreeParser(Transformer<EntityTreeParser, T> xform) { 321 EntityTreeParser entityTreeParser; 322 try { 323 entityTreeParser = treeParserPool.borrowObject(); 324 } catch (Exception e) { 325 throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); 326 } 327 T result = xform.transform(entityTreeParser); 328 try { 329 treeParserPool.returnObject(entityTreeParser); 330 } catch (Exception e) { 331 LOG.error("Error returning EntityTreeParser to pool", e); 332 } 333 return result; 334 } 335}