/******************************************************************************* * Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com) * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v3 * which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt ******************************************************************************/ package com.opendoorlogistics.core.scripts.utils; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; import javax.swing.Icon; import com.opendoorlogistics.api.ODLApi; import com.opendoorlogistics.api.StringConventions; import com.opendoorlogistics.api.components.ODLComponent; import com.opendoorlogistics.api.scripts.ScriptAdapter.ScriptAdapterType; import com.opendoorlogistics.api.tables.ODLDatastore; import com.opendoorlogistics.api.tables.ODLDatastoreAlterable; import com.opendoorlogistics.api.tables.ODLTableAlterable; import com.opendoorlogistics.api.tables.ODLTableDefinition; import com.opendoorlogistics.api.tables.ODLTableDefinitionAlterable; import com.opendoorlogistics.api.tables.TableFlags; import com.opendoorlogistics.core.api.impl.ODLApiImpl; import com.opendoorlogistics.core.components.ODLGlobalComponents; import com.opendoorlogistics.core.scripts.ScriptConstants; import com.opendoorlogistics.core.scripts.elements.AdaptedTableConfig; import com.opendoorlogistics.core.scripts.elements.AdapterConfig; import com.opendoorlogistics.core.scripts.elements.ComponentConfig; import com.opendoorlogistics.core.scripts.elements.InstructionConfig; import com.opendoorlogistics.core.scripts.elements.Option; import com.opendoorlogistics.core.scripts.elements.OutputConfig; import com.opendoorlogistics.core.scripts.elements.Script; import com.opendoorlogistics.core.scripts.elements.ScriptEditorType; import com.opendoorlogistics.core.scripts.execution.adapters.vls.VLSBuilder; import com.opendoorlogistics.core.scripts.io.ScriptIO; import com.opendoorlogistics.core.scripts.wizard.ScriptGenerator; import com.opendoorlogistics.core.tables.ODLFactory; import com.opendoorlogistics.core.tables.utils.DatastoreCopier; import com.opendoorlogistics.core.tables.utils.TableUtils; import com.opendoorlogistics.core.utils.Pair; import com.opendoorlogistics.core.utils.strings.StandardisedStringSet; import com.opendoorlogistics.core.utils.strings.Strings; import com.opendoorlogistics.core.utils.strings.Strings.DoesStringExist; /** * Methods which parse the script but are tolerant if the script contains errors (unlike actual compilation). * * @author Phil * */ final public class ScriptUtils { private ScriptUtils() { } public static void deleteOption(Option root, final Option optionToDelete) { visitOptions(root, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { for (Option child : option.getOptions()) { if (child == optionToDelete) { option.getOptions().remove(optionToDelete); break; } } return true; } }); } public static boolean hasComponentFlag(final ODLApi api, Option root, final long searchForFlags) { Boolean result = getScriptElement(new FindScriptElement<Boolean>() { @Override public Boolean find(Option option) { for (InstructionConfig instructionConfig : option.getInstructions()) { ODLComponent component = getComponent(instructionConfig); if (component == null) { throw new RuntimeException("Unknown component referenced in script: " + instructionConfig.getComponent()); } long flags = component.getFlags(api, instructionConfig.getExecutionMode()); if ((flags & searchForFlags) == searchForFlags) { return true; } } return null; } }, root); if (result != null) { return result; } return false; } // public static List<TableName> getTableNames(Iterable<TableFieldName> tablefields, boolean isReferencableDatastore){ // TreeMap<Integer, TableName> tmp = new TreeMap<>(); // for(TableFieldName o : tablefields){ // if(isReferencableDatastore && Strings.isEmpty(o.getDatastore())){ // continue; // } // if(tmp.containsKey(o.getTableIndx())==false){ // tmp.put(o.getTableIndx(), o); // } // } // return new ArrayList<TableName>(tmp.values()); // } // public static List<String> getFieldNames(Iterable<TableFieldName> tablefields, String datastore, String table){ // TreeSet<String> ret = new TreeSet<>(); // for(TableFieldName o : tablefields){ // if(Strings.equalsStandardised(datastore, o.getDatastore()) && Strings.equalsStandardised(table, o.getTableName())){ // if(Strings.isEmpty(o.fieldname)){ // ret.add(o.fieldname); // } // } // } // return new ArrayList<String>(ret); // } // public static class AbstractGenerateListingCallback implements GenerateListingCallback { // // @Override // public boolean includeExternalDatastore() { // return true; // } // // @Override // public boolean includeInstructionInput(ComponentConfig config) { // return true; // } // // @Override // public boolean includeInstructionOutput(ComponentConfig config) { // return true; // } // // @Override // public boolean includeAdapter(AdapterConfig config) { // return true; // } // // } // public static List<TableFieldName> generateTableFieldListing(Script script, boolean includeInstructionInputs, // ODLDatastore<? extends ODLTableDefinition> external) { // return generateTableFieldListing(script, includeInstructionInputs, external, null); // } public static ODLDatastore<? extends ODLTableDefinition> getIODatastoreDfn(ODLApi api, Option root, InstructionConfig instruction) { return getIOOrOutputDatastore(api, root, instruction, true); } public static ODLDatastore<? extends ODLTableDefinition> getOutputDatastoreDfn(ODLApi api, Option root, InstructionConfig instruction) { return getIOOrOutputDatastore(api, root, instruction, false); } private static ODLDatastore<? extends ODLTableDefinition> getIOOrOutputDatastore(ODLApi api, Option root, InstructionConfig instruction, boolean getIO) { ODLComponent component = getComponent(instruction); if (component != null) { Serializable config = getComponentConfig(root, instruction); if (component.getConfigClass() == null) { config = null; } else if (component.getConfigClass().isInstance(config) == false) { config = null; } if (component.getConfigClass() == null || config != null) { if (getIO) { return component.getIODsDefinition(api, config); } else { return component.getOutputDsDefinition(api, instruction.getExecutionMode(), config); } } } return null; } public static ODLComponent getComponent(ComponentConfig instruction) { ODLComponent component = ODLGlobalComponents.getProvider().getComponent(instruction.getComponent()); return component; } public static int getOptionsCount(Option option) { int ret = 1; for (Option child : option.getOptions()) { ret += getOptionsCount(child); } return ret; } public static Icon getComponentIcon(ODLApi api, String componentid, int mode) { ODLComponent comp = ODLGlobalComponents.getProvider().getComponent(componentid); if (comp != null) { return comp.getIcon(api, mode); } return null; } /** * Get the component name if available * * @param instruction * @return */ public static String getComponentName(ComponentConfig instruction) { ODLComponent component = getComponent(instruction); if (component != null) { return component.getName(); } return null; } /** * Can the script option be run? * * @param option * @return */ public static boolean isRunnableOption(Option option) { // only leaf nodes are runnable return option != null && option.getOptions().size() == 0; } public static ArrayList<Option> getRunnableOptions(Script script) { final ArrayList<Option> ret = new ArrayList<>(); visitOptions(script, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { if (isRunnableOption(option)) { ret.add(option); } return true; } }); return ret; } public static interface OptionVisitor { /** * @param parent * @param option * @param depth * @return True if child options should be visited */ boolean visitOption(Option parent, Option option, int depth); } /** * Return a deep copy of the script with its child options removed * * @param script * @return */ public static Script removeChildOptions(Script script) { script = ScriptIO.instance().deepCopy(script); script.getOptions().clear(); return script; } public static String getOptionIdByAdapterId(Option root, final String adapterId) { return getScriptElement(new FindScriptElement<String>() { @Override public String find(Option option) { for (AdapterConfig a : option.getAdapters()) { if (Strings.equalsStd(adapterId, a.getId())) { return option.getOptionId(); } } return null; } }, root); } public static String getOptionIdByInstructionId(Option root, String instructionId) { ArrayList<String> tmp = new ArrayList<>(1); tmp.add(instructionId); String[] ret = getOptionIdsByInstructionIds(root, tmp); if (ret.length > 0) { return ret[0]; } return null; } public static String[] getOptionIdsByInstructionIds(Option root, Iterable<String> instructionIds) { final StandardisedStringSet set = new StandardisedStringSet(false); final StandardisedStringSet searchFor = new StandardisedStringSet(false,instructionIds); visitOptions(root, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { for (InstructionConfig instruct : option.getInstructions()) { if (searchFor.contains(instruct.getUuid())) { set.add(option.getOptionId()); } } return true; } }); return set.toArray(); } public static interface InstructionVisitor { void visitInstruction(Option parentOption, Option option, InstructionConfig instruction); } public static void visitInstructions(Option option, final InstructionVisitor visitor) { visitOptions(option, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { for (InstructionConfig instruction : option.getInstructions()) { visitor.visitInstruction(parent, option, instruction); } return true; } }); } public static interface CopyTablesVisitor { void visitCopyTables(Option parentOption, Option option, OutputConfig copy); } public static void visitCopyTables(Option option, final CopyTablesVisitor visitor) { visitOptions(option, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { for (OutputConfig copy : option.getOutputs()) { visitor.visitCopyTables(parent, option, copy); } return true; } }); } public static interface AdaptersVisitor { void visitAdapter(Option parentOption, Option option, AdapterConfig copy); } public static void visitAdapters(Option option, final AdaptersVisitor visitor) { visitOptions(option, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { for (AdapterConfig adapter : option.getAdapters()) { visitor.visitAdapter(parent, option, adapter); } return true; } }); } public static void visitOptions(Option option, final OptionVisitor visitor) { class Parser { void parse(Option parent, Option option, int depth) { if(visitor.visitOption(parent, option, depth)){ for (Option child : option.getOptions()) { parse(option, child,depth+1); } } } } new Parser().parse(null, option,0); } public static void setAllUnsynced(Option root) { ScriptUtils.visitOptions(root, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { option.setSynchronised(false); return true; } }); } public static Option getOption(Script script, final String optionId) { List<Option> path = getOptionPath(script, optionId); if (path != null) { return path.get(path.size() - 1); } return null; } public interface OptionPredicate { boolean accept(Option option); } public interface FindScriptElement<T> { T find(Option option); } /** * Gets the adapter id using a table in the adapter. The search table is checked against each adapted table in the script using the == operator * (i.e. it must be the same object). * * @param root * @param tableInAdapter * @return */ public static String getAdapterId(Option root, final AdaptedTableConfig tableInAdapter) { return getScriptElement(new FindScriptElement<String>() { @Override public String find(Option option) { for (AdapterConfig adapter : option.getAdapters()) { for (AdaptedTableConfig table : adapter.getTables()) { if (table == tableInAdapter) { return adapter.getId(); } } } return null; } }, root); } public static <T> T getScriptElement(FindScriptElement<T> finder, Option root) { T ret = finder.find(root); if (ret != null) { return ret; } for (Option child : root.getOptions()) { ret = getScriptElement(finder, child); if (ret != null) { return ret; } } return null; } /** * Find the path to the option, including the option as the last element, or return null if not found * * @param script * @param optionId * @return */ public static List<Option> getOptionPath(Option script, final OptionPredicate predicate) { class Parser { List<Option> parse(Option option, List<Option> path) { path = new ArrayList<>(path); path.add(option); if (predicate.accept(option)) { return path; } for (Option child : option.getOptions()) { List<Option> ret = parse(child, path); if (ret != null) { return ret; } } return null; } } return new Parser().parse(script, new ArrayList<Option>()); } /** * Find the path to the option, including the option as the last element, or return null if not found * * @param script * @param optionId * @return */ public static List<Option> getOptionPath(Option script, final String optionId) { return getOptionPath(script, new OptionPredicate() { @Override public boolean accept(Option option) { return Strings.equalsStd(option.getOptionId(), optionId); } }); } public static List<String> getOptionIdsByInstructionExecutionMode(Script script, final int mode) { final ArrayList<String> ret = new ArrayList<>(); visitOptions(script, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { boolean found = false; for (InstructionConfig instruction : option.getInstructions()) { if (instruction.getExecutionMode() == mode) { found = true; break; } } if (found) { ret.add(option.getOptionId()); } return true; } }); return ret; } // /** // * Tests if the input adapters directly read from the input table. Note that indirect reads such as can happen in a lookup formula are not // * checked. // * // * @param adapters // * @param datastore // * @param tableName // * @return // */ // public static boolean getReadsDirectlyTable(Iterable<AdapterConfig> adapters, String datastore, String tableName) { // for (AdapterConfig adapterConfig : adapters) { // for (AdaptedTableConfig table : adapterConfig) { // if (Strings.equalsStd(datastore, table.getFromDatastore()) && Strings.equalsStd(tableName, table.getFromTable())) { // return true; // } // } // } // // return false; // } /** * Returns the first matching optionid for the input name * @param root * @param optionName * @return */ public static String getOptionIdByName(Option root, String optionName){ class Ret{ String id; } Ret ret = new Ret(); visitOptions(root, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { if(ret.id==null && Strings.equalsStd(optionName, option.getName())){ ret.id = option.getOptionId(); } // keep on parsing unless we've found the first one return ret.id ==null; } }); return ret.id; } public static ComponentConfig getComponentConfig(Option root, final String configId) { return getScriptElement(new FindScriptElement<ComponentConfig>() { @Override public ComponentConfig find(Option option) { for (ComponentConfig c : option.getComponentConfigs()) { if (Strings.equalsStd(c.getConfigId(), configId)) { return c; } } return null; } }, root); } public static AdapterConfig getAdapterById(Option option, String id, boolean recurseChildOptions) { // find in current level for (AdapterConfig ac : option.getAdapters()) { if (Strings.equalsStd(ac.getId(), id)) { return ac; } } // recurse if (recurseChildOptions) { for (Option child : option.getOptions()) { AdapterConfig ac = getAdapterById(child, id, true); if (ac != null) { return ac; } } } return null; } public static boolean hasDatastore(Script script, String id) { if (Strings.equalsStd(ScriptConstants.EXTERNAL_DS_NAME, id)) { return true; } for (InstructionConfig instruction : script.getInstructions()) { if (Strings.equalsStd(instruction.getOutputDatastore(), id)) { return true; } } return false; } // public static boolean hasAdapterReadingTable(Script script, String datastore, String table) { // for (AdapterConfig adapterConfig : script.getAdapters()) { // for (AdaptedTableConfig tableConfig : adapterConfig.getTables()) { // if (Strings.equalsStd(datastore, tableConfig.getFromDatastore())) { // if (Strings.equalsStd(table, tableConfig.getFromTable())) { // return true; // } // } // } // } // return false; // } /** * Find the instruction's configuration. This could be internal (inside the component object) or external - an id referencing a data object * earlier in the script. * * @param root * @param instruction * @return */ public static Serializable getComponentConfig(Option root, final InstructionConfig instruction) { Serializable ret = null; if (Strings.isEmpty(instruction.getConfigId())) { ret = instruction.getComponentConfig(); } else { // search for config List<Option> available = getOptionPath(root, new OptionPredicate() { @Override public boolean accept(Option option) { for (InstructionConfig test : option.getInstructions()) { if (test == instruction) { return true; } } return false; } }); if (available == null || available.size() == 0) { throw new RuntimeException("Corrupt script - cannot find instruction in script."); } // find the config int n = available.size(); boolean found = false; for (int i = n - 1; found == false && i >= 0; i--) { for (ComponentConfig conf : available.get(i).getComponentConfigs()) { if (Strings.equalsStd(conf.getConfigId(), instruction.getConfigId())) { ret = conf.getComponentConfig(); found = true; break; } } } if (!found) { throw new RuntimeException("Corrupt script - cannot find component configuration with id: " + instruction.getConfigId()); } } // return default object if we have none if (ret == null) { ODLComponent component = getComponent(instruction); if(component!=null){ if(component.getConfigClass()!=null){ try { ret = component.getConfigClass().newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } } } return ret; } /** * Create a new component configuration object if the instruction doesn't already have one or its of the wrong class * * @param component * @param componentConfig * @return */ public static void validateComponentConfigClass(ODLComponent component, ComponentConfig componentConfig) { if (component.getConfigClass() == null) { // no config used componentConfig.setComponentConfig(null); return; } boolean isInstruction = InstructionConfig.class.isInstance(componentConfig); if (isInstruction && Strings.isEmpty(componentConfig.getConfigId()) == false) { // its an instruction which uses external config componentConfig.setComponentConfig(null); return; } // check correct class Serializable obj = componentConfig.getComponentConfig(); if (obj != null && component.getConfigClass() != null && component.getConfigClass().isInstance(obj) == false) { obj = null; } // create new object if missing one if (obj == null) { try { obj = component.getConfigClass().newInstance(); } catch (Throwable e) { obj = null; } } componentConfig.setComponentConfig(obj); } /** * Gets the icon from a script if has a single instruction with an Icon * * @return */ public static Icon getIconFromMasterComponent(ODLApi api, Option option) { Pair<ODLComponent, Integer> pair = getMasterComponent(option); if (pair != null && pair.getFirst() != null && pair.getSecond() != null) { return pair.getFirst().getIcon(api, pair.getSecond()); } return null; } // public static String getShortDisplayName(Script script) { // // if (script.getScriptEditorUIType() == ScriptEditorType.GENERAL_EDITOR) { // // return "General"; // // } // // Pair<ODLComponent, Integer> master = getMasterComponent(script); // if (master != null) { // ODLComponent component = master.getFirst(); // if (component != null) { // return component.getName(); // // } // } // // return ""; // } // public static Pair<String,Integer> getMasterComponentId(Option option) { // if(Script.class.isInstance(option)){ // Script script = (Script)option; // if(!Strings.isEmpty(script.getCreatedByComponentId())){ // return new Pair<String, Integer>(script.getCreatedByComponentId(), ODLComponent.MODE_DEFAULT); // } // } // // InstructionConfig config = option.getLastInstruction(); // if (config != null) { // return new Pair<String,Integer>( config.getComponent(), config.getExecutionMode()); // } // // return null; // } private static Pair<ODLComponent, Integer> getMasterComponent(Option option) { Pair<String, Integer> id = null; if (Script.class.isInstance(option)) { Script script = (Script) option; if (!Strings.isEmpty(script.getCreatedByComponentId())) { id = new Pair<String, Integer>(script.getCreatedByComponentId(), ODLComponent.MODE_DEFAULT); } } if (id == null) { InstructionConfig config = option.getLastInstruction(); if (config != null) { id = new Pair<String, Integer>(config.getComponent(), config.getExecutionMode()); } } if (id != null && id.getFirst() != null) { return new Pair<ODLComponent, Integer>(ODLGlobalComponents.getProvider().getComponent(id.getFirst()), id.getSecond()); } return null; } /** * Validates a script's synchronisation settings, returning false if script is invalid and settings cannot be fixed */ public static boolean validateSynchonisation(final ODLApi api, final Script script) { class RetValue { boolean ret = true; } final RetValue retValue = new RetValue(); visitOptions(script, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { OutputWindowSyncLevel syncLevel = getOutputWindowSyncLevel(api, script, option.getOptionId()); switch (syncLevel) { case NEVER: option.setSynchronised(false); break; case ALWAYS: option.setSynchronised(true); break; case MANUAL: break; case ERROR: retValue.ret = false; break; } return true; } }); return retValue.ret; } public enum OutputWindowSyncLevel { NEVER, MANUAL, ALWAYS, ERROR; } private enum IdType { INSTRUCTION, ADAPTER, INSTRUCTION_CONFIGURATION, OPTION } public static void validateIds(final Script script) { final StandardisedStringSet[] sets = new StandardisedStringSet[IdType.values().length]; for (int i = 0; i < sets.length; i++) { sets[i] = new StandardisedStringSet(false); } visitOptions(script, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { validate(option.getOptionId(), IdType.OPTION); for (AdapterConfig adapterConfig : option.getAdapters()) { validate(adapterConfig.getId(), IdType.ADAPTER); } for (InstructionConfig instructionConfig : option.getInstructions()) { validate(instructionConfig.getUuid(), IdType.INSTRUCTION); } for (ComponentConfig componentConfig : option.getComponentConfigs()) { validate(componentConfig.getConfigId(), IdType.INSTRUCTION_CONFIGURATION); } return true; } void validate(String id, IdType type) { if (Strings.isEmpty(id)) { throw new RuntimeException("Script contains an empty id for an " + Strings.convertEnumToDisplayFriendly(type) + "."); } int i = type.ordinal(); if (sets[i].contains(id)) { throw new RuntimeException("Script contains a duplicate id " + id + " for an " + Strings.convertEnumToDisplayFriendly(type) + "."); } sets[i].add(id); } }); } public static InstructionConfig getInstructionByUUID(Option root, final String uuid) { return getScriptElement(new FindScriptElement<InstructionConfig>() { @Override public InstructionConfig find(Option option) { for (InstructionConfig instruction : option.getInstructions()) { if (Strings.equalsStd(instruction.getUuid(), uuid)) { return instruction; } } return null; } }, root); } public static String createUniqueInstructionId(Option root) { while (true) { String id = UUID.randomUUID().toString(); if (getInstructionByUUID(root, id) == null) { return id; } } } /** * Create a unique datastore id. This is checked against (a) the external datastore name, (b) all adapters in the root option and below and (c) * all instruction outputs in the root option and below * * @param root * @param baseId * @return */ public static String createUniqueDatastoreId(final Option root, String baseId) { return Strings.makeUnique(baseId, new DoesStringExist() { @Override public boolean isExisting(final String s) { if (Strings.equalsStd(s, ScriptConstants.EXTERNAL_DS_NAME)) { return true; } if (getAdapterById(root, s, true) != null) { return true; } if (getInstructionByOutputDs(root, s) != null) { return true; } return false; } }); } public static class ScriptIds{ final public Set<String> adapters; final public Set<String> outputdatastores; final public Set<String> options; final public Set<String> datastoresAndAdapters; ScriptIds(Set<String> adapters, Set<String> outputdatastores, Set<String> options, Set<String> datastoresAndAdapters) { this.adapters = adapters; this.outputdatastores = outputdatastores; this.options = options; this.datastoresAndAdapters = datastoresAndAdapters; } public boolean containsAdapterOrDatastore(String s){ return adapters.contains(s) || outputdatastores.contains(s); } public static Set<String> getCommonStrings(ODLApi api,Set<String> setA, Set<String> setB){ Set<String> ret = api.stringConventions().createStandardisedSet(); for(String s : setA){ if(setB.contains(s)){ ret.add(s); } } for(String s : setB){ if(setA.contains(s)){ ret.add(s); } } return ret; } } /** * Fetch all ids in the script * @param api * @param option * @return */ public static ScriptIds getIds(ODLApi api,Option option){ StringConventions strings = api.stringConventions(); ScriptIds ret = new ScriptIds(strings.createStandardisedSet(), strings.createStandardisedSet(), strings.createStandardisedSet(), strings.createStandardisedSet()); visitOptions(option, new OptionVisitor() { @Override public boolean visitOption(Option parent, Option option, int depth) { ret.options.add(option.getOptionId()); for(InstructionConfig instruction : option.getInstructions()){ if(instruction.getOutputDatastore()!=null){ ret.outputdatastores.add(instruction.getOutputDatastore()); } } for(AdapterConfig adapter : option.getAdapters()){ if(adapter.getId()!=null){ ret.adapters.add(adapter.getId()); } } return true; } }); ret.datastoresAndAdapters.addAll(ret.outputdatastores); ret.datastoresAndAdapters.addAll(ret.adapters); return ret; } /** * For the input script find out what synchronisation can be done of output windows * * @param script * @param optionId * @return */ public static OutputWindowSyncLevel getOutputWindowSyncLevel(ODLApi api, Script script, String optionId) { List<Option> path = getOptionPath(script, optionId); return getOutputWindowSyncLevel(api, path); } /** * @param path * @return */ public static OutputWindowSyncLevel getOutputWindowSyncLevel(ODLApi api, List<Option> path) { if (path == null) { return OutputWindowSyncLevel.ERROR; } int alwaysCount = 0; int neverCount = 0; int canBe = 0; for (Option option : path) { for (InstructionConfig instruction : option.getInstructions()) { ODLComponent component = getComponent(instruction); if (component == null) { return OutputWindowSyncLevel.ERROR; } long flags = component.getFlags(api, instruction.getExecutionMode()); if ((flags & ODLComponent.FLAG_OUTPUT_WINDOWS_ALWAYS_SYNCHRONISED) == ODLComponent.FLAG_OUTPUT_WINDOWS_ALWAYS_SYNCHRONISED) { alwaysCount++; } else if ((flags & ODLComponent.FLAG_OUTPUT_WINDOWS_CAN_BE_SYNCHRONISED) != ODLComponent.FLAG_OUTPUT_WINDOWS_CAN_BE_SYNCHRONISED) { neverCount++; } else { canBe++; } } } // 8 possiblities exist if (alwaysCount == 0 && neverCount == 0 && canBe == 0) { return OutputWindowSyncLevel.NEVER; } if (alwaysCount == 0 && neverCount == 0 && canBe >= 1) { return OutputWindowSyncLevel.MANUAL; } if (alwaysCount == 0 && neverCount >= 1 && canBe >= 0) { return OutputWindowSyncLevel.NEVER; } if (alwaysCount == 0 && neverCount >= 1 && canBe >= 1) { return OutputWindowSyncLevel.NEVER; } if (alwaysCount >= 1 && neverCount == 0 && canBe == 0) { return OutputWindowSyncLevel.ALWAYS; } if (alwaysCount >= 1 && neverCount == 0 && canBe >= 1) { return OutputWindowSyncLevel.ALWAYS; } if (alwaysCount >= 1 && neverCount >= 1 && canBe >= 0) { return OutputWindowSyncLevel.ERROR; } if (alwaysCount >= 1 && neverCount >= 1 && canBe >= 1) { return OutputWindowSyncLevel.ERROR; } // should never get to here... throw new UnsupportedOperationException(); } public static <T extends ODLTableDefinition> Iterable<T> tableIterator(final ODLDatastore<T> ds) { return new Iterable<T>() { @Override public Iterator<T> iterator() { return new Iterator<T>() { int currentIndex = -1; @Override public boolean hasNext() { return (currentIndex + 1) < ds.getTableCount(); } @Override public T next() { return ds.getTableAt(++currentIndex); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } public static String getDefaultScriptName(Script script) { Pair<ODLComponent, Integer> component = getMasterComponent(script); if (component != null && component.getFirst() != null) { return component.getFirst().getName(); } return "script"; } // public static boolean getReadsExternalDatastore(final ODLApi api, Script script) { // for (AdapterConfig adapterConfig : script.getAdapters()) { // for (AdaptedTableConfig table : adapterConfig.getTables()) { // if (ScriptConstants.isExternalDs(table.getFromDatastore())) { // return true; // } // } // } // // for (InstructionConfig instruction : script.getInstructions()) { // try { // ODLComponent component = ScriptUtils.getComponent(instruction); // if (component != null) { // Serializable componentConfig = ScriptUtils.getComponentConfig(script, instruction); // if (component.getIODsDefinition(api, componentConfig) != null && ScriptConstants.isExternalDs(instruction.getDatastore())) { // return true; // } // } // // } catch (Throwable e) { // // TODO: handle exception // } // } // return false; // } /** * Create a provider for the adapter's expected structure, based on whether its a VLS adapter or * the component which uses it. The expected structure is recreated dynamically on-the-fly, * so should always be up-to-date. * @param api * @param root * @param optionContainingAdapter * @param adapterId * @return */ public static AdapterExpectedStructureProvider createAdapterExpectedStructure(final ODLApi api, final Option root, final Option optionContainingAdapter, final String adapterId) { // find the adapter return new AdapterExpectedStructureProvider() { @Override public ODLDatastore<? extends ODLTableDefinition> getDatastoreDefinition() { final ODLDatastoreAlterable<? extends ODLTableAlterable> ret = ODLFactory.createAlterable(); // Check if the adapter is drawable AdapterConfig adapterConfig = getAdapterById(optionContainingAdapter, adapterId, false); if(adapterConfig.getAdapterType() == ScriptAdapterType.VLS){ // Add view-layer-style tables api.tables().addTableDefinitions(VLSBuilder.getVLSTableDefinitions(), ret, false); ODLTableDefinition src = VLSBuilder.getSourceTableDefinition(); api.tables().addTableDefinition(src, ret, false); return ret; } else if(adapterConfig.getAdapterType() == ScriptAdapterType.PARAMETER){ return api.scripts().parameters().dsDefinition(false); } if ((adapterConfig.getFlags() & TableFlags.FLAG_IS_DRAWABLES) == TableFlags.FLAG_IS_DRAWABLES) { ODLDatastore<? extends ODLTableDefinition> drawables = api.standardComponents().map().getLayeredDrawablesDefinition(); for (int i = 0; i < drawables.getTableCount(); i++) { api.tables().copyTableDefinition(drawables.getTableAt(i), ret); } } // Find all instructions which are children of the input option and use the adapter id. visitInstructions(optionContainingAdapter, new InstructionVisitor() { @Override public void visitInstruction(Option parentOption, Option option, InstructionConfig instruction) { // does the instruction use the adapter id? if (Strings.equalsStd(instruction.getDatastore(), adapterId)) { ODLDatastore<? extends ODLTableDefinition> iods = getIODatastoreDfn(api, root, instruction); if (iods != null) { // add any additional flags we find (e.g. wildcard flag, is reporter flag) ret.setFlags(ret.getFlags() | iods.getFlags()); for (ODLTableDefinition targetTable : tableIterator(iods)) { // find the table in the return datastore and create it if not existing ODLTableDefinitionAlterable retTable = TableUtils.findTable(ret, targetTable.getName()); if (retTable == null) { retTable = DatastoreCopier.copyTableDefinition(targetTable, ret); } // add column wildcard if we have it... if (TableUtils.hasFlag(targetTable, TableFlags.FLAG_COLUMN_WILDCARD)) { retTable.setFlags(retTable.getFlags() | TableFlags.FLAG_COLUMN_WILDCARD); } // now copy any missing columns over (happens when more than 1 instruction uses the same adapter) int nc = targetTable.getColumnCount(); for (int col = 0; col < nc; col++) { String colName = targetTable.getColumnName(col); if (TableUtils.findColumnIndx(retTable, colName) == -1) { DatastoreCopier.copyColumnDefinition(targetTable, retTable, col, false); } } } } } // what about adapters which take this as input??? } }); return ret; } }; } public static String createUniqueTableName(final AdapterConfig adapter, String base) { return Strings.makeUnique(base, new DoesStringExist() { @Override public boolean isExisting(String s) { for (AdaptedTableConfig table : adapter) { if (Strings.equalsStd(table.getName(), s)) { return true; } } return false; } }); } /** * @param root * @param dsId * @return */ private static InstructionConfig getInstructionByOutputDs(final Option root, final String dsId) { return getScriptElement(new FindScriptElement<InstructionConfig>() { @Override public InstructionConfig find(Option option) { for (InstructionConfig instructionConfig : option.getInstructions()) { if (Strings.equalsStd(instructionConfig.getOutputDatastore(), dsId)) { return instructionConfig; } } return null; } }, root); } }