diff options
Diffstat (limited to 'media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java')
-rw-r--r-- | media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java b/media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java new file mode 100644 index 0000000..366ef82 --- /dev/null +++ b/media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.filterfw.io; + +import java.lang.Float; +import java.lang.Integer; +import java.lang.String; + +import java.util.ArrayList; +import java.util.regex.Pattern; + +import android.filterfw.core.Filter; +import android.filterfw.core.FilterFactory; +import android.filterfw.core.FilterGraph; +import android.filterfw.core.KeyValueMap; +import android.filterfw.core.ProtocolException; +import android.filterfw.io.GraphReader; +import android.filterfw.io.GraphIOException; +import android.filterfw.io.PatternScanner; + +/** + * @hide + */ +public class TextGraphReader extends GraphReader { + + private ArrayList<Command> mCommands = new ArrayList<Command>(); + private Filter mCurrentFilter; + private FilterGraph mCurrentGraph; + private KeyValueMap mBoundReferences; + private KeyValueMap mSettings; + private FilterFactory mFactory; + + private interface Command { + public void execute(TextGraphReader reader) throws GraphIOException; + } + + private class ImportPackageCommand implements Command { + private String mPackageName; + + public ImportPackageCommand(String packageName) { + mPackageName = packageName; + } + + @Override + public void execute(TextGraphReader reader) throws GraphIOException { + try { + reader.mFactory.addPackage(mPackageName); + } catch (IllegalArgumentException e) { + throw new GraphIOException(e.getMessage()); + } + } + } + + private class AddLibraryCommand implements Command { + private String mLibraryName; + + public AddLibraryCommand(String libraryName) { + mLibraryName = libraryName; + } + + @Override + public void execute(TextGraphReader reader) { + reader.mFactory.addFilterLibrary(mLibraryName); + } + } + + private class AllocateFilterCommand implements Command { + private String mClassName; + private String mFilterName; + + public AllocateFilterCommand(String className, String filterName) { + mClassName = className; + mFilterName = filterName; + } + + public void execute(TextGraphReader reader) throws GraphIOException { + // Create the filter + Filter filter = null; + try { + filter = reader.mFactory.createFilterByClassName(mClassName, mFilterName); + } catch (IllegalArgumentException e) { + throw new GraphIOException(e.getMessage()); + } + + // Set it as the current filter + reader.mCurrentFilter = filter; + } + } + + private class InitFilterCommand implements Command { + private KeyValueMap mParams; + + public InitFilterCommand(KeyValueMap params) { + mParams = params; + } + + @Override + public void execute(TextGraphReader reader) throws GraphIOException { + Filter filter = reader.mCurrentFilter; + try { + filter.initWithValueMap(mParams); + } catch (ProtocolException e) { + throw new GraphIOException(e.getMessage()); + } + reader.mCurrentGraph.addFilter(mCurrentFilter); + } + } + + private class ConnectCommand implements Command { + private String mSourceFilter; + private String mSourcePort; + private String mTargetFilter; + private String mTargetName; + + public ConnectCommand(String sourceFilter, + String sourcePort, + String targetFilter, + String targetName) { + mSourceFilter = sourceFilter; + mSourcePort = sourcePort; + mTargetFilter = targetFilter; + mTargetName = targetName; + } + + @Override + public void execute(TextGraphReader reader) { + reader.mCurrentGraph.connect(mSourceFilter, mSourcePort, mTargetFilter, mTargetName); + } + } + + @Override + public FilterGraph readGraphString(String graphString) throws GraphIOException { + FilterGraph result = new FilterGraph(); + + reset(); + mCurrentGraph = result; + parseString(graphString); + applySettings(); + executeCommands(); + reset(); + + return result; + } + + private void reset() { + mCurrentGraph = null; + mCurrentFilter = null; + mCommands.clear(); + mBoundReferences = new KeyValueMap(); + mSettings = new KeyValueMap(); + mFactory = new FilterFactory(); + } + + private void parseString(String graphString) throws GraphIOException { + final Pattern commandPattern = Pattern.compile("@[a-zA-Z]+"); + final Pattern curlyClosePattern = Pattern.compile("\\}"); + final Pattern curlyOpenPattern = Pattern.compile("\\{"); + final Pattern ignorePattern = Pattern.compile("(\\s+|//[^\\n]*\\n)+"); + final Pattern packageNamePattern = Pattern.compile("[a-zA-Z\\.]+"); + final Pattern libraryNamePattern = Pattern.compile("[a-zA-Z\\./:]+"); + final Pattern portPattern = Pattern.compile("\\[[a-zA-Z0-9\\-_]+\\]"); + final Pattern rightArrowPattern = Pattern.compile("=>"); + final Pattern semicolonPattern = Pattern.compile(";"); + final Pattern wordPattern = Pattern.compile("[a-zA-Z0-9\\-_]+"); + + final int STATE_COMMAND = 0; + final int STATE_IMPORT_PKG = 1; + final int STATE_ADD_LIBRARY = 2; + final int STATE_FILTER_CLASS = 3; + final int STATE_FILTER_NAME = 4; + final int STATE_CURLY_OPEN = 5; + final int STATE_PARAMETERS = 6; + final int STATE_CURLY_CLOSE = 7; + final int STATE_SOURCE_FILTERNAME = 8; + final int STATE_SOURCE_PORT = 9; + final int STATE_RIGHT_ARROW = 10; + final int STATE_TARGET_FILTERNAME = 11; + final int STATE_TARGET_PORT = 12; + final int STATE_ASSIGNMENT = 13; + final int STATE_EXTERNAL = 14; + final int STATE_SETTING = 15; + final int STATE_SEMICOLON = 16; + + int state = STATE_COMMAND; + PatternScanner scanner = new PatternScanner(graphString, ignorePattern); + + String curClassName = null; + String curSourceFilterName = null; + String curSourcePortName = null; + String curTargetFilterName = null; + String curTargetPortName = null; + + // State machine main loop + while (!scanner.atEnd()) { + switch (state) { + case STATE_COMMAND: { + String curCommand = scanner.eat(commandPattern, "<command>"); + if (curCommand.equals("@import")) { + state = STATE_IMPORT_PKG; + } else if (curCommand.equals("@library")) { + state = STATE_ADD_LIBRARY; + } else if (curCommand.equals("@filter")) { + state = STATE_FILTER_CLASS; + } else if (curCommand.equals("@connect")) { + state = STATE_SOURCE_FILTERNAME; + } else if (curCommand.equals("@set")) { + state = STATE_ASSIGNMENT; + } else if (curCommand.equals("@external")) { + state = STATE_EXTERNAL; + } else if (curCommand.equals("@setting")) { + state = STATE_SETTING; + } else { + throw new GraphIOException("Unknown command '" + curCommand + "'!"); + } + break; + } + + case STATE_IMPORT_PKG: { + String packageName = scanner.eat(packageNamePattern, "<package-name>"); + mCommands.add(new ImportPackageCommand(packageName)); + state = STATE_SEMICOLON; + break; + } + + case STATE_ADD_LIBRARY: { + String libraryName = scanner.eat(libraryNamePattern, "<library-name>"); + mCommands.add(new AddLibraryCommand(libraryName)); + state = STATE_SEMICOLON; + break; + } + + case STATE_FILTER_CLASS: + curClassName = scanner.eat(wordPattern, "<class-name>"); + state = STATE_FILTER_NAME; + break; + + case STATE_FILTER_NAME: { + String curFilterName = scanner.eat(wordPattern, "<filter-name>"); + mCommands.add(new AllocateFilterCommand(curClassName, curFilterName)); + state = STATE_CURLY_OPEN; + break; + } + + case STATE_CURLY_OPEN: + scanner.eat(curlyOpenPattern, "{"); + state = STATE_PARAMETERS; + break; + + case STATE_PARAMETERS: { + KeyValueMap params = readKeyValueAssignments(scanner, curlyClosePattern); + mCommands.add(new InitFilterCommand(params)); + state = STATE_CURLY_CLOSE; + break; + } + + case STATE_CURLY_CLOSE: + scanner.eat(curlyClosePattern, "}"); + state = STATE_COMMAND; + break; + + case STATE_SOURCE_FILTERNAME: + curSourceFilterName = scanner.eat(wordPattern, "<source-filter-name>"); + state = STATE_SOURCE_PORT; + break; + + case STATE_SOURCE_PORT: { + String portString = scanner.eat(portPattern, "[<source-port-name>]"); + curSourcePortName = portString.substring(1, portString.length() - 1); + state = STATE_RIGHT_ARROW; + break; + } + + case STATE_RIGHT_ARROW: + scanner.eat(rightArrowPattern, "=>"); + state = STATE_TARGET_FILTERNAME; + break; + + case STATE_TARGET_FILTERNAME: + curTargetFilterName = scanner.eat(wordPattern, "<target-filter-name>"); + state = STATE_TARGET_PORT; + break; + + case STATE_TARGET_PORT: { + String portString = scanner.eat(portPattern, "[<target-port-name>]"); + curTargetPortName = portString.substring(1, portString.length() - 1); + mCommands.add(new ConnectCommand(curSourceFilterName, + curSourcePortName, + curTargetFilterName, + curTargetPortName)); + state = STATE_SEMICOLON; + break; + } + + case STATE_ASSIGNMENT: { + KeyValueMap assignment = readKeyValueAssignments(scanner, semicolonPattern); + mBoundReferences.putAll(assignment); + state = STATE_SEMICOLON; + break; + } + + case STATE_EXTERNAL: { + String externalName = scanner.eat(wordPattern, "<external-identifier>"); + bindExternal(externalName); + state = STATE_SEMICOLON; + break; + } + + case STATE_SETTING: { + KeyValueMap setting = readKeyValueAssignments(scanner, semicolonPattern); + mSettings.putAll(setting); + state = STATE_SEMICOLON; + break; + } + + case STATE_SEMICOLON: + scanner.eat(semicolonPattern, ";"); + state = STATE_COMMAND; + break; + } + } + + // Make sure end of input was expected + if (state != STATE_SEMICOLON && state != STATE_COMMAND) { + throw new GraphIOException("Unexpected end of input!"); + } + } + + @Override + public KeyValueMap readKeyValueAssignments(String assignments) throws GraphIOException { + final Pattern ignorePattern = Pattern.compile("\\s+"); + PatternScanner scanner = new PatternScanner(assignments, ignorePattern); + return readKeyValueAssignments(scanner, null); + } + + private KeyValueMap readKeyValueAssignments(PatternScanner scanner, + Pattern endPattern) throws GraphIOException { + // Our parser is a state-machine, and these are our states + final int STATE_IDENTIFIER = 0; + final int STATE_EQUALS = 1; + final int STATE_VALUE = 2; + final int STATE_POST_VALUE = 3; + + final Pattern equalsPattern = Pattern.compile("="); + final Pattern semicolonPattern = Pattern.compile(";"); + final Pattern wordPattern = Pattern.compile("[a-zA-Z]+[a-zA-Z0-9]*"); + final Pattern stringPattern = Pattern.compile("'[^']*'|\\\"[^\\\"]*\\\""); + final Pattern intPattern = Pattern.compile("[0-9]+"); + final Pattern floatPattern = Pattern.compile("[0-9]*\\.[0-9]+f?"); + final Pattern referencePattern = Pattern.compile("\\$[a-zA-Z]+[a-zA-Z0-9]"); + final Pattern booleanPattern = Pattern.compile("true|false"); + + int state = STATE_IDENTIFIER; + KeyValueMap newVals = new KeyValueMap(); + String curKey = null; + String curValue = null; + + while (!scanner.atEnd() && !(endPattern != null && scanner.peek(endPattern))) { + switch (state) { + case STATE_IDENTIFIER: + curKey = scanner.eat(wordPattern, "<identifier>"); + state = STATE_EQUALS; + break; + + case STATE_EQUALS: + scanner.eat(equalsPattern, "="); + state = STATE_VALUE; + break; + + case STATE_VALUE: + if ((curValue = scanner.tryEat(stringPattern)) != null) { + newVals.put(curKey, curValue.substring(1, curValue.length() - 1)); + } else if ((curValue = scanner.tryEat(referencePattern)) != null) { + String refName = curValue.substring(1, curValue.length()); + Object referencedObject = mBoundReferences != null + ? mBoundReferences.get(refName) + : null; + if (referencedObject == null) { + throw new GraphIOException( + "Unknown object reference to '" + refName + "'!"); + } + newVals.put(curKey, referencedObject); + } else if ((curValue = scanner.tryEat(booleanPattern)) != null) { + newVals.put(curKey, Boolean.parseBoolean(curValue)); + } else if ((curValue = scanner.tryEat(floatPattern)) != null) { + newVals.put(curKey, Float.parseFloat(curValue)); + } else if ((curValue = scanner.tryEat(intPattern)) != null) { + newVals.put(curKey, Integer.parseInt(curValue)); + } else { + throw new GraphIOException(scanner.unexpectedTokenMessage("<value>")); + } + state = STATE_POST_VALUE; + break; + + case STATE_POST_VALUE: + scanner.eat(semicolonPattern, ";"); + state = STATE_IDENTIFIER; + break; + } + } + + // Make sure end is expected + if (state != STATE_IDENTIFIER && state != STATE_POST_VALUE) { + throw new GraphIOException( + "Unexpected end of assignments on line " + scanner.lineNo() + "!"); + } + + return newVals; + } + + private void bindExternal(String name) throws GraphIOException { + if (mReferences.containsKey(name)) { + Object value = mReferences.get(name); + mBoundReferences.put(name, value); + } else { + throw new GraphIOException("Unknown external variable '" + name + "'! " + + "You must add a reference to this external in the host program using " + + "addReference(...)!"); + } + } + + /** + * Unused for now: Often you may want to declare references that are NOT in a certain graph, + * e.g. when reading multiple graphs with the same reader. We could print a warning, but even + * that may be too much. + **/ + private void checkReferences() throws GraphIOException { + for (String reference : mReferences.keySet()) { + if (!mBoundReferences.containsKey(reference)) { + throw new GraphIOException( + "Host program specifies reference to '" + reference + "', which is not " + + "declared @external in graph file!"); + } + } + } + + private void applySettings() throws GraphIOException { + for (String setting : mSettings.keySet()) { + Object value = mSettings.get(setting); + if (setting.equals("autoBranch")) { + expectSettingClass(setting, value, String.class); + if (value.equals("synced")) { + mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_SYNCED); + } else if (value.equals("unsynced")) { + mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_UNSYNCED); + } else if (value.equals("off")) { + mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_OFF); + } else { + throw new GraphIOException("Unknown autobranch setting: " + value + "!"); + } + } else if (setting.equals("discardUnconnectedOutputs")) { + expectSettingClass(setting, value, Boolean.class); + mCurrentGraph.setDiscardUnconnectedOutputs((Boolean)value); + } else { + throw new GraphIOException("Unknown @setting '" + setting + "'!"); + } + } + } + + private void expectSettingClass(String setting, + Object value, + Class expectedClass) throws GraphIOException { + if (value.getClass() != expectedClass) { + throw new GraphIOException("Setting '" + setting + "' must have a value of type " + + expectedClass.getSimpleName() + ", but found a value of type " + + value.getClass().getSimpleName() + "!"); + } + } + + private void executeCommands() throws GraphIOException { + for (Command command : mCommands) { + command.execute(this); + } + } +} |