summaryrefslogtreecommitdiffstats
path: root/media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java
diff options
context:
space:
mode:
Diffstat (limited to 'media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java')
-rw-r--r--media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java489
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);
+ }
+ }
+}