/******************************************************************************* * Copyright (c) 2007, 2011 Symbian Software Limited and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bala Torati (Symbian) - Initial API and implementation * Christian Walther (walther@indel.ch) [333537] - Macro expansion in conditional process groups *******************************************************************************/ package org.eclipse.cdt.core.templateengine.process; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.templateengine.TemplateCore; import org.eclipse.cdt.core.templateengine.TemplateDescriptor; import org.eclipse.cdt.core.templateengine.TemplateEngine; import org.eclipse.cdt.core.templateengine.TemplateEngineMessages; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.w3c.dom.Element; /** * ConditionalProcess encloses an <if condition="..."></if> block of the template. * The currently supported conditions are equals and not equals operations performed on two * Strings. The respective operators are == and !=. Any spaces will be treated as part of the * operands. The two operands will be evaluated for simple String equals and not equals after * performing a single pass replace of any replace markers with their values in the template's * value store. */ public class ConditionalProcessGroup { private TemplateCore template; private Set<String> macros; private String conditionString; private String lValue; private String rValue; private Operator operator; private List<Process> processes; private String id; /** * @author BalaT */ private static class Operator { final static Operator EQUALS = new Operator("="); //$NON-NLS-1$ final static Operator NOT_EQUALS = new Operator("!="); //$NON-NLS-1$ String id; Operator(String id) { this.id = id; } @Override public boolean equals(Object arg0) { if(arg0 instanceof Operator) { return id.equals(((Operator)arg0).id); } return false; } } /** * Constructs a ConditionalProcess element from the supplied conditionElement (<if>) while building Process * objects out of each of the element's <process> children. */ public ConditionalProcessGroup(TemplateCore template, Element conditionElement, int id) { this.id = "Condition " + id; //$NON-NLS-1$ conditionString = conditionElement.getAttribute(ProcessHelper.CONDITION); if (conditionString != null) { if (conditionString.trim().equals("")) { //$NON-NLS-1$ conditionString = null; } else { int op = conditionString.indexOf(ProcessHelper.EQUALS); if (op != -1) { this.operator = Operator.EQUALS; lValue = conditionString.substring(0, op); rValue = conditionString.substring(op + ProcessHelper.EQUALS.length()); } else { op = conditionString.indexOf(ProcessHelper.NOT_EQUALS); if (op != -1) { this.operator = Operator.NOT_EQUALS; lValue = conditionString.substring(0, op); rValue = conditionString.substring(op + ProcessHelper.NOT_EQUALS.length()); }//else an unsupported operation where this condition is ignored. } collectMacros(lValue); collectMacros(rValue); } } createProcessObjects(template, TemplateEngine.getChildrenOfElementByTag(conditionElement, TemplateDescriptor.PROCESS)); } /** * Adds values passed as parameter to the macros object * @param value */ private void collectMacros(String value) { if (value != null) { if (macros == null) { macros = new HashSet<String>(); } macros.addAll(ProcessHelper.getReplaceKeys(value)); } } /** * Constructs a ConditionalProcess element from the supplied process elements while building Process * objects out of each of the supplied process elements (<process>). The condition in this case is evaluated to true. * * This Constructor is expected to be used to evaluate all those process elements that are children of the template root element. */ public ConditionalProcessGroup(TemplateCore template, Element[] processElements) { id = "No Condition"; //$NON-NLS-1$ createProcessObjects(template, Arrays.asList(processElements)); } /** * Creates the Process from the process Elements. * @param templateCore * @param processElements */ private void createProcessObjects(TemplateCore templateCore, List<Element> processElements) { this.template = templateCore; this.processes = new ArrayList<Process>(processElements.size()); for (int j = 0, l = processElements.size(); j < l; j++) { Element processElem = processElements.get(j); if (processElem.getNodeName().equals(TemplateDescriptor.PROCESS)) { String processId = id + "--> Process " + (j + 1) + " (" + processElem.getAttribute(Process.ELEM_TYPE) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ processes.add(new Process(templateCore, processElem, processId)); } } } /** * Checks if this conditional process group is completely ready to be processed. */ public boolean isReadyToProcess() { return areMacrosForConditionEvaluationExpandable() && isConditionValueTrue() && areProcessesReady(); } /** * * @return boolean, as true if the Processes are ready to process */ private boolean areProcessesReady() { for(Process process : processes) { if (!process.isReadyToProcess()) { return false; } } return true; } /** * * @return boolean, true if Macros For Condition Evaluation Expandable. */ private boolean areMacrosForConditionEvaluationExpandable() { if (macros != null) { Map<String, String> valueStore = template.getValueStore(); for(String value : macros) { if (valueStore.get(value) == null) { return false; } } } return true; } /** * * @return boolean, true if Condition Value is True. */ public boolean isConditionValueTrue() { if (conditionString == null) { return true; } if (!areMacrosForConditionEvaluationExpandable()) { return false; } Map<String, String> valueStore = template.getValueStore(); String processedLValue = ProcessHelper.getValueAfterExpandingMacros(lValue, macros, valueStore); String processedRValue = ProcessHelper.getValueAfterExpandingMacros(rValue, macros, valueStore); if(operator.equals(Operator.EQUALS)) { return processedLValue.equals(processedRValue); } else if(operator.equals(Operator.NOT_EQUALS)) { return !processedLValue.equals(processedRValue); } else { return false; } } /** * Process and Returns the Status of the prosses as a List. * @param monitor * @return List contains the IStatus. * @throws ProcessFailureException */ public List<IStatus> process(IProgressMonitor monitor) throws ProcessFailureException { if (!areMacrosForConditionEvaluationExpandable()) { throw new ProcessFailureException(getUnexpandableMacroMessage()); } if (!isConditionValueTrue()) { List<IStatus> statuses = new ArrayList<IStatus>(1); statuses.add(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, IStatus.INFO, TemplateEngineMessages.getString("ConditionalProcessGroup.notExecuting") + id, null)); //$NON-NLS-1$ return statuses; } List<IStatus> statuses = new ArrayList<IStatus>(processes.size()); for(Process process : processes) { try { statuses.add(process.process(monitor)); } catch (ProcessFailureException e) { throw new ProcessFailureException(e.getMessage(), e, statuses); } } return statuses; } /** * Return the Unexpandable Macro Message * @return */ private String getUnexpandableMacroMessage() { if (macros != null) { Map<String, String> valueStore = template.getValueStore(); for(String value : macros) { if (valueStore.get(value) == null) { return TemplateEngineMessages.getString("ConditionalProcessGroup.unexpandableMacro") + value; //$NON-NLS-1$ } } } return null; } /** * Returns the Macros as a Set. * @return Set, contains macros */ public Set<String> getMacros() { return macros; } /** * @return the union of all macros used in the child processes */ public Set<String> getAllMacros() { Set<String> set = null; if (macros != null) { set = new HashSet<String>(); set.addAll(macros); } for(Process process : processes) { Set<String> subSet = process.getMacros(); if (subSet != null) { if (set == null) { set = new HashSet<String>(); } set.addAll(subSet); } } return set; } }