001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.oozie.coord.input.logic;
020
021import java.util.List;
022
023import org.apache.commons.lang.StringUtils;
024import org.jdom.Element;
025import org.jdom.Namespace;
026
027/**
028 * Parses xml into jexl expression
029 */
030public class InputLogicParser {
031
032    public final static String COORD_INPUT_EVENTS_DATA_IN = "data-in";
033
034    public final static String AND = "and";
035
036    public final static String OR = "or";
037
038    public final static String COMBINE = "combine";
039
040    /**
041     * Parses the xml.
042     *
043     * @param root the root
044     * @return the string
045     */
046    public String parse(Element root) {
047        return parseWithName(root, null);
048
049    }
050
051    /**
052     * Parses the xml with name.
053     *
054     * @param root the root
055     * @param name the name
056     * @return the string
057     */
058    @SuppressWarnings("unchecked")
059    public String parseWithName(Element root, String name) {
060        if (root == null) {
061            return "";
062        }
063        StringBuffer parsedString = new StringBuffer();
064
065        List<Element> childrens = root.getChildren();
066        for (int i = 0; i < childrens.size(); i++) {
067            String childName = childrens.get(i).getAttributeValue("name");
068            String min = childrens.get(i).getAttributeValue("min");
069            String wait = childrens.get(i).getAttributeValue("wait");
070
071            if (name == null || name.equals(childName)) {
072                parsedString.append(parse(childrens.get(i), getOpt(childrens.get(i).getName()), min, wait));
073            }
074            else {
075                parsedString.append(parseWithName(childrens.get(i), name));
076            }
077        }
078        return parsedString.toString();
079    }
080
081    public String parse(Element root, String opt, String min, String wait) {
082        StringBuffer parsedString = new StringBuffer();
083
084        Namespace ns = root.getNamespace();
085        if (root.getName().equals(COMBINE)) {
086            parsedString.append("(");
087            parsedString.append(processCombinedNode(root, getOpt(root.getName()), getMin(root, min),
088                    getWait(root, wait)));
089            parsedString.append(")");
090        }
091        else if (root.getName().equals(AND) || root.getName().equals(OR)) {
092            parsedString.append("(");
093            parsedString.append(parseAllChildren(root, opt, getOpt(root.getName()), getMin(root, min),
094                    getWait(root, wait)));
095            parsedString.append(")");
096
097        }
098        else if (root.getChild(COORD_INPUT_EVENTS_DATA_IN, ns) != null) {
099            parsedString.append("(");
100            parsedString.append(processChildNode(root, getOpt(root.getName()), getMin(root, min), getWait(root, wait)));
101            parsedString.append(")");
102        }
103        else if (root.getName().equals(COORD_INPUT_EVENTS_DATA_IN)) {
104            parsedString.append(parseDataInNode(root, min, wait));
105
106        }
107        return parsedString.toString();
108
109    }
110
111    /**
112     * Parses the all children.
113     *
114     * @param root the root
115     * @param parentOpt the parent opt
116     * @param opt the opt
117     * @param min the min
118     * @param wait the wait
119     * @return the string
120     */
121    @SuppressWarnings("unchecked")
122    private String parseAllChildren(Element root, String parentOpt, String opt, String min, String wait) {
123        StringBuffer parsedString = new StringBuffer();
124
125        List<Element> childrens = root.getChildren();
126        for (int i = 0; i < childrens.size(); i++) {
127            String currentMin = min;
128            String currentWait = wait;
129            String childMin = childrens.get(i).getAttributeValue("min");
130            String childWait = childrens.get(i).getAttributeValue("wait");
131            if (!StringUtils.isEmpty(childMin)) {
132                currentMin = childMin;
133            }
134            if (!StringUtils.isEmpty(childWait)) {
135                currentWait = childWait;
136            }
137            parsedString.append(parse(childrens.get(i), opt, currentMin, currentWait));
138            if (i < childrens.size() - 1) {
139                if (!StringUtils.isEmpty(opt))
140                    parsedString.append(" " + opt + " ");
141            }
142        }
143        return parsedString.toString();
144
145    }
146
147    /**
148     * Parses the data in node.
149     *
150     * @param root the root
151     * @param min the min
152     * @param wait the wait
153     * @return the string
154     */
155    private String parseDataInNode(Element root, String min, String wait) {
156        StringBuffer parsedString = new StringBuffer();
157
158        String nestedChildDataName = root.getAttributeValue("dataset");
159
160        parsedString.append("dependencyBuilder.input(\"" + nestedChildDataName + "\")");
161        appendMin(root, min, parsedString);
162        appendWait(root, wait, parsedString);
163        parsedString.append(".build()");
164        return parsedString.toString();
165    }
166
167    /**
168     * Process child node.
169     *
170     * @param root the root
171     * @param opt the opt
172     * @param min the min
173     * @param wait the wait
174     * @return the string
175     */
176    @SuppressWarnings("unchecked")
177    private String processChildNode(final Element root, final String opt, final String min, final String wait) {
178        StringBuffer parsedString = new StringBuffer();
179
180        Namespace ns = root.getNamespace();
181
182        List<Element> childrens = root.getChildren(COORD_INPUT_EVENTS_DATA_IN, ns);
183
184        for (int i = 0; i < childrens.size(); i++) {
185            parsedString.append(parseDataInNode(childrens.get(i), min, wait));
186
187            if (i < childrens.size() - 1) {
188                parsedString.append(" " + opt + " ");
189            }
190        }
191        return parsedString.toString();
192    }
193
194    /**
195     * Process combined node.
196     *
197     * @param root the root
198     * @param opt the opt
199     * @param min the min
200     * @param wait the wait
201     * @return the string
202     */
203    @SuppressWarnings("unchecked")
204    private String processCombinedNode(final Element root, final String opt, final String min, final String wait) {
205        StringBuffer parsedString = new StringBuffer();
206
207        Namespace ns = root.getNamespace();
208
209        List<Element> childrens = root.getChildren(COORD_INPUT_EVENTS_DATA_IN, ns);
210        parsedString.append("dependencyBuilder.combine(");
211
212        for (int i = 0; i < childrens.size(); i++) {
213            String nestedChildDataName = childrens.get(i).getAttributeValue("dataset");
214            parsedString.append("\"" + nestedChildDataName + "\"");
215            if (i < childrens.size() - 1) {
216                parsedString.append(",");
217            }
218        }
219        parsedString.append(")");
220
221        appendMin(root, min, parsedString);
222        appendWait(root, wait, parsedString);
223        parsedString.append(".build()");
224        return parsedString.toString();
225
226    }
227
228    /**
229     * Gets the opt.
230     *
231     * @param opt the opt
232     * @return the opt
233     */
234    private String getOpt(String opt) {
235        if (opt.equalsIgnoreCase("or")) {
236            return "||";
237        }
238
239        if (opt.equalsIgnoreCase("and")) {
240            return "&&";
241        }
242
243        return "";
244
245    }
246
247    /**
248     * Gets the min.
249     *
250     * @param root the root
251     * @param parentMin the parent min
252     * @return the min
253     */
254    private String getMin(Element root, String parentMin) {
255        String min = root.getAttributeValue("min");
256        if (StringUtils.isEmpty(min)) {
257            return parentMin;
258        }
259        return min;
260
261    }
262
263    /**
264     * Gets the wait.
265     *
266     * @param root the root
267     * @param parentWait the parent wait
268     * @return the wait
269     */
270    private String getWait(Element root, String parentWait) {
271        String wait = root.getAttributeValue("wait");
272        if (StringUtils.isEmpty(parentWait)) {
273            return parentWait;
274        }
275        return wait;
276
277    }
278
279    private void appendWait(final Element root, String wait, StringBuffer parsedString) {
280        String childWait = root.getAttributeValue("wait");
281        if (!StringUtils.isEmpty(childWait)) {
282            parsedString.append(".inputWait(" + childWait + ")");
283
284        }
285        else {
286            if (!StringUtils.isEmpty(wait)) {
287                parsedString.append(".inputWait(" + wait + ")");
288
289            }
290        }
291
292    }
293
294    private void appendMin(final Element root, String min, StringBuffer parsedString) {
295        String childMin = root.getAttributeValue("min");
296
297        if (!StringUtils.isEmpty(childMin)) {
298            parsedString.append(".min(" + childMin + ")");
299
300        }
301        else {
302            if (!StringUtils.isEmpty(min)) {
303                parsedString.append(".min(" + min + ")");
304
305            }
306        }
307    }
308
309}