// This product is provided under the terms of EPL (Eclipse Public License) // version 1.0. // // The full license text can be read from: http://www.eclipse.org/org/documents/epl-v10.php package org.dtangler.genericengine.dependenciesstreamparser; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.dtangler.core.exception.DtException; import org.dtangler.genericengine.types.Item; import org.dtangler.genericengine.types.ValidScopes; /** * Parser for the dependency definitions stream. * * Dtangler universal frontend API syntax: * * dependencies : (dependencyDefinition | itemDefinition)* dependencyDefinition * : dependant+ ':' dependee+ '\n' itemDefinition : dependable dependant : * dependable dependee : dependable dependaple : displayname | scope '{' * fullyqualifiedname '}' fullyqualifiedname : parentfqn? displayname parentfqn * : fullyqualifiedname displayname : string * */ public class ItemDependenciesStreamParser { public Set<Item> parse(ValidScopes validScopes, File dependencyFile, String encoding) { return parse(validScopes, getBufferedIOReader(dependencyFile), encoding); } public Set<Item> parse(ValidScopes validScopes, String encoding) { return parse(validScopes, getBufferedIOReader(), encoding); } private BufferedReader getBufferedIOReader(File file) { if (file == null || file.getAbsolutePath() == null) throw new DtException( "could not read text file input: file name not specified"); try { return new BufferedReader(new FileReader(file.getAbsolutePath())); } catch (FileNotFoundException e) { throw new DtException("could not read from file " + file.getAbsolutePath() + ": " + e.getMessage()); } } private BufferedReader getBufferedIOReader() { return new BufferedReader(new InputStreamReader(System.in)); } private void addItemScopeToValidScopes(ValidScopes validScopes, Item item) { String scopeExisting = validScopes.getScopeName(item.getScopeIndex()); if (scopeExisting == null) { validScopes.setScopeName(item.getScope(), item.getScopeIndex()); } else { if (!scopeExisting.equals(item.getScope())) throw new DtException("scope \"" + item.getScope() + "\" already exists with the name \"" + scopeExisting + "\" at level " + (item.getScopeIndex()+1)); } } private Item getNewItem(ValidScopes validScopes, String scope, String displayname, String[] parentDisplaynames, String encoding) { Item item = new Item(scope, displayname, parentDisplaynames, encoding); addItemScopeToValidScopes(validScopes, item); return item; } public List<Item> parseItem(ValidScopes validScopes, String itemDefinition, String encoding) { final String nonItemCharsRegex = "\\{\\}\\:\\s"; final String anyNonItemCharRegex = "[" + nonItemCharsRegex + "]"; final String anyNonItemCharAtLeastOnceRegex = anyNonItemCharRegex + "+"; final String anyItemCharRegex = "[^" + nonItemCharsRegex + "]"; final String anyItemRegex = "[\\s]*" + anyItemCharRegex + "+" + "[\\s]*"; final String anyNumberOfItemsRegex = "[\\s]*(" + anyItemRegex + ")*"; final String onceOrNotAtAllItemsRegex = "[\\s]*(" + anyItemRegex + ")?"; final String itemDefinitionWithScopeRegex = onceOrNotAtAllItemsRegex + "\\{" + anyNumberOfItemsRegex + "\\}[\\s]*"; final String itemDefinitionWithScopeAtLeastOnceRegex = "(" + itemDefinitionWithScopeRegex + ")+"; final String itemDefinitionWithoutScopeRegex = "(" + anyItemRegex + ")+"; if (itemDefinition == null) throw new DtException("invalid item definition: null"); List<Item> items = new ArrayList<Item>(); String[] parents = null; String item = null; String scope = null; if (itemDefinition.matches(itemDefinitionWithScopeAtLeastOnceRegex)) { Pattern p = Pattern.compile(itemDefinitionWithScopeRegex); Matcher m = p.matcher(itemDefinition); while (m.find()) { String[] words = itemDefinition.substring(m.start(), m.end()) .trim().split(anyNonItemCharAtLeastOnceRegex); if (words == null || words.length < 2) throw new DtException("invalid item definition: \"" + itemDefinition + "\""); scope = words[0]; item = words[words.length - 1]; if (words.length > 2) { parents = new String[words.length - 2]; for (int iParent = 0, iWord = 0; iWord < words.length; iWord++) { if (iWord > 0 && iWord < words.length - 1) parents[iParent++] = words[iWord]; } } items.add(getNewItem(validScopes, scope, item, parents, encoding)); } return items; } else if (itemDefinition.matches(itemDefinitionWithoutScopeRegex)) { scope = ""; String[] words = itemDefinition.trim().split( anyNonItemCharAtLeastOnceRegex); if (words == null || words.length <= 0) { throw new DtException("invalid item definition: \"" + itemDefinition + "\""); } for (String word : words) { if (word == null || word.length() == 0) continue; items.add(getNewItem(validScopes, scope, word.trim(), parents, encoding)); } return items; } else { throw new DtException("invalid item definition: \"" + itemDefinition + "\""); } } private Item getExistingItemFromSet(Set<Item> items, Item item) { if (items != null) { for (Item itemInSet : items) { if (itemInSet == null) continue; if (itemInSet.equals(item)) { return itemInSet; } } } return item; } private void saveItemsToSet(Set<Item> allItems, List<Item> itemDependantList, List<Item> itemDependeeList) { for (Item itemDependant : itemDependantList) { if (allItems.contains(itemDependant)) { itemDependant = getExistingItemFromSet(allItems, itemDependant); } else { allItems.add(itemDependant); } if (itemDependeeList != null) { for (Item itemDependee : itemDependeeList) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItemFromSet(allItems, itemDependee); } else { allItems.add(itemDependee); } itemDependant.addDependency(itemDependee); } } } } private void parseDependencyOrItemDefinition(ValidScopes validScopes, Set<Item> allItems, String dependencyOrItemDefinition, String encoding) { final String itemDelimiterRegex = "\\:"; if (dependencyOrItemDefinition == null) throw new DtException("invalid dependency or item definition: null"); String[] items = dependencyOrItemDefinition.split(itemDelimiterRegex); if (items == null || !(items.length >= 1 && items.length <= 2)) throw new DtException("invalid dependency or item definition: \"" + dependencyOrItemDefinition + "\""); if (items.length == 1) { // item definition saveItemsToSet(allItems, parseItem(validScopes, items[0], encoding), null); } else if (items.length == 2) { // item dependency definition saveItemsToSet(allItems, parseItem(validScopes, items[0], encoding), parseItem( validScopes, items[1], encoding)); } return; } public Set<Item> parse(ValidScopes validScopes, BufferedReader in, String encoding) { if (in == null) throw new DtException("unable to read from stream"); Set<Item> items = new HashSet<Item>(); String line; int lineNo = 0; try { while ((line = in.readLine()) != null) { lineNo++; if (line.length() < 1) continue; parseDependencyOrItemDefinition(validScopes, items, line, encoding); } } catch (IOException e) { throw new DtException("error in reading the item dependencies: " + e.getMessage()); } catch (DtException e) { throw new DtException( "error in parsing the item dependencies in line (" + lineNo + "): " + e.getMessage()); } finally { try { in.close(); } catch (IOException e) { throw new DtException( "error in reading the item dependencies: " + e.getMessage()); } } return items; } }