View Javadoc
1   /*
2    * (c) Copyright 2006-2020 by rapiddweller GmbH & Volker Bergmann. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, is permitted under the terms of the
6    * GNU General Public License.
7    *
8    * For redistributing this software or a derivative work under a license other
9    * than the GPL-compatible Free Software License as defined by the Free
10   * Software Foundation or approved by OSI, you must first obtain a commercial
11   * license to this software product from rapiddweller GmbH & Volker Bergmann.
12   *
13   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14   * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
15   * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
17   * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24   * POSSIBILITY OF SUCH DAMAGE.
25   */
26  
27  package com.rapiddweller.benerator.engine.parser.xml;
28  
29  import com.rapiddweller.benerator.engine.BeneratorContext;
30  import com.rapiddweller.benerator.engine.BeneratorRootStatement;
31  import com.rapiddweller.benerator.engine.ResourceManager;
32  import com.rapiddweller.benerator.engine.Statement;
33  import com.rapiddweller.benerator.engine.statement.BeanStatement;
34  import com.rapiddweller.benerator.engine.statement.IfStatement;
35  import com.rapiddweller.common.CollectionUtil;
36  import com.rapiddweller.common.ConfigurationError;
37  import com.rapiddweller.common.ConversionException;
38  import com.rapiddweller.common.ParseException;
39  import com.rapiddweller.common.StringUtil;
40  import com.rapiddweller.common.xml.XMLUtil;
41  import com.rapiddweller.script.Assignment;
42  import com.rapiddweller.script.BeanSpec;
43  import com.rapiddweller.script.DatabeneScriptParser;
44  import com.rapiddweller.script.Expression;
45  import com.rapiddweller.script.expression.BeanConstruction;
46  import com.rapiddweller.script.expression.DefaultConstruction;
47  import com.rapiddweller.script.expression.ExpressionUtil;
48  import org.apache.logging.log4j.LogManager;
49  import org.apache.logging.log4j.Logger;
50  import org.w3c.dom.Element;
51  
52  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_CLASS;
53  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_ID;
54  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_SPEC;
55  import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_BEAN;
56  import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_PROPERTY;
57  
58  /**
59   * Parses a &lt;bean&gt; element.<br/><br/>
60   * Created: 25.10.2009 01:09:59
61   *
62   * @author Volker Bergmann
63   * @since 0.6.0
64   */
65  public class BeanParser extends AbstractBeneratorDescriptorParser {
66  
67    private static final Logger logger = LogManager.getLogger(BeanParser.class);
68  
69    /**
70     * Instantiates a new Bean parser.
71     */
72    public BeanParser() {
73      // only allowed in non-loop statements in order to avoid leaks
74      super(EL_BEAN, CollectionUtil.toSet(ATT_ID), CollectionUtil.toSet(ATT_CLASS, ATT_SPEC),
75          BeneratorRootStatement.class, IfStatement.class);
76    }
77  
78    /**
79     * Parse bean expression expression.
80     *
81     * @param element the element
82     * @return the expression
83     */
84    @SuppressWarnings({"unchecked", "rawtypes"})
85    public static Expression<?> parseBeanExpression(Element element) {
86      String id = element.getAttribute(ATT_ID);
87      Expression<?> instantiation = null;
88      String beanSpec = element.getAttribute(ATT_SPEC);
89      String beanClass = element.getAttribute(ATT_CLASS);
90      if (!StringUtil.isEmpty(beanSpec)) {
91        try {
92          instantiation = DatabeneScriptParser.parseBeanSpec(beanSpec);
93        } catch (ParseException e) {
94          throw new ConfigurationError("Error parsing bean spec: " + beanSpec, e);
95        }
96      } else if (!StringUtil.isEmpty(beanClass)) {
97        logger.debug("Instantiating bean of class " + beanClass + " (id=" + id + ")");
98        instantiation = new DefaultConstruction(beanClass);
99      } else {
100       syntaxError("bean definition is missing 'class' or 'spec' attribute", element);
101     }
102     Element[] propertyElements = XMLUtil.getChildElements(element, false, EL_PROPERTY);
103     Assignment[] propertyInitializers = mapPropertyDefinitions(propertyElements);
104     return new BeanConstruction(instantiation, propertyInitializers);
105   }
106 
107   /**
108    * Resolve bean expression bean spec.
109    *
110    * @param element the element
111    * @param context the context
112    * @return the bean spec
113    */
114   @SuppressWarnings({"unchecked", "rawtypes"})
115   public static BeanSpec resolveBeanExpression(Element element, BeneratorContext context) {
116     String id = element.getAttribute(ATT_ID);
117     Expression<?> instantiation;
118     String beanSpecString = element.getAttribute(ATT_SPEC);
119     String beanClass = element.getAttribute(ATT_CLASS);
120     boolean ref = false;
121     if (!StringUtil.isEmpty(beanSpecString)) {
122       try {
123         BeanSpec spec = DatabeneScriptParser.resolveBeanSpec(beanSpecString, context);
124         instantiation = ExpressionUtil.constant(spec.getBean());
125         ref = spec.isReference();
126       } catch (ParseException e) {
127         throw new ConfigurationError("Error parsing bean spec: " + beanSpecString, e);
128       }
129     } else if (!StringUtil.isEmpty(beanClass)) {
130       logger.debug("Instantiating bean of class " + beanClass + " (id=" + id + ")");
131       instantiation = new DefaultConstruction<>(beanClass);
132     } else {
133       throw new ConfigurationError("Syntax error in definition of bean " + id);
134     }
135     Element[] propertyElements = XMLUtil.getChildElements(element);
136     for (Element propertyElement : propertyElements) {
137       if (!EL_PROPERTY.equals(propertyElement.getNodeName())) {
138         syntaxError("not a supported bean child element: <" + propertyElement.getNodeName() + ">",
139             propertyElement);
140       }
141     }
142     Assignment[] propertyInitializers = mapPropertyDefinitions(propertyElements);
143     Object result = new BeanConstruction(instantiation, propertyInitializers).evaluate(context);
144     return new BeanSpec(result, ref);
145   }
146 
147   /**
148    * Map property definitions assignment [ ].
149    *
150    * @param propertyElements the property elements
151    * @return the assignment [ ]
152    */
153   public static Assignment[] mapPropertyDefinitions(Element[] propertyElements) {
154     Assignment[] assignments = new Assignment[propertyElements.length];
155     for (int i = 0; i < propertyElements.length; i++) {
156       assignments[i] = parseProperty(propertyElements[i]);
157     }
158     return assignments;
159   }
160 
161   private static Assignment parseProperty(Element propertyElement) {
162     String propertyName = propertyElement.getAttribute("name");
163     Expression<?> value = SettingParser.parseValue(propertyElement);
164     return new Assignment(propertyName, value);
165   }
166 
167   @Override
168   public BeanStatement doParse(Element element, Statement[] parentPath, BeneratorParseContext context) {
169     try {
170       String id = element.getAttribute(ATT_ID);
171       ResourceManager resourceManager = context.getResourceManager();
172       Expression<?> bean = parseBeanExpression(element);
173       return new BeanStatement(id, bean, resourceManager);
174     } catch (ConversionException e) {
175       throw new ConfigurationError(e);
176     }
177   }
178 
179 }