165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn/*
265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Copyright (C) 2011 The Android Open Source Project
365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Licensed under the Apache License, Version 2.0 (the "License");
565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * you may not use this file except in compliance with the License.
665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * You may obtain a copy of the License at
765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *      http://www.apache.org/licenses/LICENSE-2.0
965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn *
1065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * Unless required by applicable law or agreed to in writing, software
1165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * distributed under the License is distributed on an "AS IS" BASIS,
1265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * See the License for the specific language governing permissions and
1465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * limitations under the License.
1565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn */
1665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
1765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
1865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennpackage android.filterfw.io;
1965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
2065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.lang.Float;
2165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.lang.Integer;
2265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.lang.String;
2365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
2465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.ArrayList;
2565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport java.util.regex.Pattern;
2665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
2765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.Filter;
2865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FilterFactory;
2965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.FilterGraph;
3065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.KeyValueMap;
3165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.core.ProtocolException;
3265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.io.GraphReader;
3365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.io.GraphIOException;
3465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennimport android.filterfw.io.PatternScanner;
3565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
3665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn/**
3765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn * @hide
3865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn */
3965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Rennpublic class TextGraphReader extends GraphReader {
4065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private ArrayList<Command> mCommands = new ArrayList<Command>();
4265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private Filter mCurrentFilter;
4365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private FilterGraph mCurrentGraph;
4465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private KeyValueMap mBoundReferences;
4565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private KeyValueMap mSettings;
4665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private FilterFactory mFactory;
4765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
4865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private interface Command {
4965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void execute(TextGraphReader reader) throws GraphIOException;
5065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
5165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private class ImportPackageCommand implements Command {
5365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private String mPackageName;
5465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public ImportPackageCommand(String packageName) {
5665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mPackageName = packageName;
5765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
5865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
5965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        @Override
6065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void execute(TextGraphReader reader) throws GraphIOException {
6165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            try {
6265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                reader.mFactory.addPackage(mPackageName);
6365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } catch (IllegalArgumentException e) {
6465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new GraphIOException(e.getMessage());
6565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
6665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
6765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
6865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
6965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private class AddLibraryCommand implements Command {
7065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private String mLibraryName;
7165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
7265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public AddLibraryCommand(String libraryName) {
7365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mLibraryName = libraryName;
7465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
7565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
7665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        @Override
7765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void execute(TextGraphReader reader) {
7865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            reader.mFactory.addFilterLibrary(mLibraryName);
7965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
8065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
8165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
8265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private class AllocateFilterCommand implements Command {
8365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private String mClassName;
8465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private String mFilterName;
8565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
8665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public AllocateFilterCommand(String className, String filterName) {
8765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mClassName = className;
8865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mFilterName = filterName;
8965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
9065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
9165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void execute(TextGraphReader reader) throws GraphIOException {
9265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Create the filter
9365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            Filter filter = null;
9465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            try {
9565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                filter = reader.mFactory.createFilterByClassName(mClassName, mFilterName);
9665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } catch (IllegalArgumentException e) {
9765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new GraphIOException(e.getMessage());
9865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
9965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
10065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            // Set it as the current filter
10165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            reader.mCurrentFilter = filter;
10265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
10365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
10465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
10565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private class InitFilterCommand implements Command {
10665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private KeyValueMap mParams;
10765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
10865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public InitFilterCommand(KeyValueMap params) {
10965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mParams = params;
11065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
11165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
11265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        @Override
11365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void execute(TextGraphReader reader) throws GraphIOException {
11465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            Filter filter = reader.mCurrentFilter;
11565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            try {
11665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                filter.initWithValueMap(mParams);
11765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } catch (ProtocolException e) {
11865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new GraphIOException(e.getMessage());
11965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
12065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            reader.mCurrentGraph.addFilter(mCurrentFilter);
12165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
12265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
12365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
12465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private class ConnectCommand implements Command {
12565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private String mSourceFilter;
12665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private String mSourcePort;
12765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private String mTargetFilter;
12865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        private String mTargetName;
12965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
13065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public ConnectCommand(String sourceFilter,
13165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                              String sourcePort,
13265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                              String targetFilter,
13365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                              String targetName) {
13465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mSourceFilter = sourceFilter;
13565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mSourcePort = sourcePort;
13665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mTargetFilter = targetFilter;
13765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mTargetName = targetName;
13865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
13965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
14065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        @Override
14165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        public void execute(TextGraphReader reader) {
14265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            reader.mCurrentGraph.connect(mSourceFilter, mSourcePort, mTargetFilter, mTargetName);
14365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
14465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
14565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
14665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
14765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public FilterGraph readGraphString(String graphString) throws GraphIOException {
14865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        FilterGraph result = new FilterGraph();
14965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
15065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        reset();
15165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCurrentGraph = result;
15265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        parseString(graphString);
15365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        applySettings();
15465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        executeCommands();
15565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        reset();
15665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
15765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return result;
15865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
15965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
16065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void reset() {
16165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCurrentGraph = null;
16265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCurrentFilter = null;
16365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mCommands.clear();
16465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mBoundReferences = new KeyValueMap();
16565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mSettings = new KeyValueMap();
16665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        mFactory = new FilterFactory();
16765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
16865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
16965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void parseString(String graphString) throws GraphIOException {
17065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern commandPattern = Pattern.compile("@[a-zA-Z]+");
17165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern curlyClosePattern = Pattern.compile("\\}");
17265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern curlyOpenPattern = Pattern.compile("\\{");
17365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern ignorePattern = Pattern.compile("(\\s+|//[^\\n]*\\n)+");
17465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern packageNamePattern = Pattern.compile("[a-zA-Z\\.]+");
17565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern libraryNamePattern = Pattern.compile("[a-zA-Z\\./:]+");
17665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern portPattern = Pattern.compile("\\[[a-zA-Z0-9\\-_]+\\]");
17765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern rightArrowPattern = Pattern.compile("=>");
17865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern semicolonPattern = Pattern.compile(";");
17965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern wordPattern = Pattern.compile("[a-zA-Z0-9\\-_]+");
18065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
18165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_COMMAND           = 0;
18265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_IMPORT_PKG        = 1;
18365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_ADD_LIBRARY       = 2;
18465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_FILTER_CLASS      = 3;
18565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_FILTER_NAME       = 4;
18665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_CURLY_OPEN        = 5;
18765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_PARAMETERS        = 6;
18865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_CURLY_CLOSE       = 7;
18965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_SOURCE_FILTERNAME = 8;
19065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_SOURCE_PORT       = 9;
19165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_RIGHT_ARROW       = 10;
19265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_TARGET_FILTERNAME = 11;
19365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_TARGET_PORT       = 12;
19465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_ASSIGNMENT        = 13;
19565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_EXTERNAL          = 14;
19665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_SETTING           = 15;
19765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_SEMICOLON         = 16;
19865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
19965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int state = STATE_COMMAND;
20065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        PatternScanner scanner = new PatternScanner(graphString, ignorePattern);
20165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        String curClassName = null;
20365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        String curSourceFilterName = null;
20465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        String curSourcePortName = null;
20565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        String curTargetFilterName = null;
20665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        String curTargetPortName = null;
20765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
20865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // State machine main loop
20965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        while (!scanner.atEnd()) {
21065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            switch (state) {
21165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_COMMAND: {
21265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    String curCommand = scanner.eat(commandPattern, "<command>");
21365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    if (curCommand.equals("@import")) {
21465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        state = STATE_IMPORT_PKG;
21565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if (curCommand.equals("@library")) {
21665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        state = STATE_ADD_LIBRARY;
21765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if (curCommand.equals("@filter")) {
21865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        state = STATE_FILTER_CLASS;
21965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if (curCommand.equals("@connect")) {
22065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        state = STATE_SOURCE_FILTERNAME;
22165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if (curCommand.equals("@set")) {
22265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        state = STATE_ASSIGNMENT;
22365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if (curCommand.equals("@external")) {
22465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        state = STATE_EXTERNAL;
22565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if (curCommand.equals("@setting")) {
22665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        state = STATE_SETTING;
22765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else {
22865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        throw new GraphIOException("Unknown command '" + curCommand + "'!");
22965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    }
23065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
23165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
23265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
23365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_IMPORT_PKG: {
23465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    String packageName = scanner.eat(packageNamePattern, "<package-name>");
23565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mCommands.add(new ImportPackageCommand(packageName));
23665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_SEMICOLON;
23765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
23865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
23965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
24065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_ADD_LIBRARY: {
24165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    String libraryName = scanner.eat(libraryNamePattern, "<library-name>");
24265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mCommands.add(new AddLibraryCommand(libraryName));
24365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_SEMICOLON;
24465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
24565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
24665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
24765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_FILTER_CLASS:
24865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    curClassName = scanner.eat(wordPattern, "<class-name>");
24965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_FILTER_NAME;
25065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
25165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
25265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_FILTER_NAME: {
25365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    String curFilterName = scanner.eat(wordPattern, "<filter-name>");
25465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mCommands.add(new AllocateFilterCommand(curClassName, curFilterName));
25565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_CURLY_OPEN;
25665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
25765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
25865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
25965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_CURLY_OPEN:
26065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    scanner.eat(curlyOpenPattern, "{");
26165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_PARAMETERS;
26265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
26365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
26465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_PARAMETERS: {
26565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    KeyValueMap params = readKeyValueAssignments(scanner, curlyClosePattern);
26665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mCommands.add(new InitFilterCommand(params));
26765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_CURLY_CLOSE;
26865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
26965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
27065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
27165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_CURLY_CLOSE:
27265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    scanner.eat(curlyClosePattern, "}");
27365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_COMMAND;
27465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
27565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
27665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_SOURCE_FILTERNAME:
27765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    curSourceFilterName = scanner.eat(wordPattern, "<source-filter-name>");
27865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_SOURCE_PORT;
27965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
28065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
28165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_SOURCE_PORT: {
28265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    String portString = scanner.eat(portPattern, "[<source-port-name>]");
28365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    curSourcePortName = portString.substring(1, portString.length() - 1);
28465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_RIGHT_ARROW;
28565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
28665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
28765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
28865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_RIGHT_ARROW:
28965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    scanner.eat(rightArrowPattern, "=>");
29065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_TARGET_FILTERNAME;
29165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
29265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
29365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_TARGET_FILTERNAME:
29465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    curTargetFilterName = scanner.eat(wordPattern, "<target-filter-name>");
29565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_TARGET_PORT;
29665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
29765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
29865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_TARGET_PORT: {
29965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    String portString = scanner.eat(portPattern, "[<target-port-name>]");
30065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    curTargetPortName = portString.substring(1, portString.length() - 1);
30165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mCommands.add(new ConnectCommand(curSourceFilterName,
30265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                     curSourcePortName,
30365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                     curTargetFilterName,
30465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                     curTargetPortName));
30565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_SEMICOLON;
30665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
30765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
30865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
30965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_ASSIGNMENT: {
31065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    KeyValueMap assignment = readKeyValueAssignments(scanner, semicolonPattern);
31165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mBoundReferences.putAll(assignment);
31265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_SEMICOLON;
31365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
31465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
31565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
31665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_EXTERNAL: {
31765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    String externalName = scanner.eat(wordPattern, "<external-identifier>");
31865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    bindExternal(externalName);
31965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_SEMICOLON;
32065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
32165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
32265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
32365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_SETTING: {
32465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    KeyValueMap setting = readKeyValueAssignments(scanner, semicolonPattern);
32565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mSettings.putAll(setting);
32665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_SEMICOLON;
32765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
32865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
32965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
33065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_SEMICOLON:
33165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    scanner.eat(semicolonPattern, ";");
33265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_COMMAND;
33365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
33465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
33565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
33665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
33765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Make sure end of input was expected
33865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (state != STATE_SEMICOLON && state != STATE_COMMAND) {
33965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new GraphIOException("Unexpected end of input!");
34065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
34165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
34265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
34365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    @Override
34465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    public KeyValueMap readKeyValueAssignments(String assignments) throws GraphIOException {
34565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern ignorePattern = Pattern.compile("\\s+");
34665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        PatternScanner scanner = new PatternScanner(assignments, ignorePattern);
34765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return readKeyValueAssignments(scanner, null);
34865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
34965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
35065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private KeyValueMap readKeyValueAssignments(PatternScanner scanner,
35165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                                Pattern endPattern) throws GraphIOException {
35265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Our parser is a state-machine, and these are our states
35365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_IDENTIFIER = 0;
35465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_EQUALS     = 1;
35565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_VALUE      = 2;
35665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final int STATE_POST_VALUE = 3;
35765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
35865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern equalsPattern = Pattern.compile("=");
35965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern semicolonPattern = Pattern.compile(";");
36065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern wordPattern = Pattern.compile("[a-zA-Z]+[a-zA-Z0-9]*");
36165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern stringPattern = Pattern.compile("'[^']*'|\\\"[^\\\"]*\\\"");
36265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern intPattern = Pattern.compile("[0-9]+");
36365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern floatPattern = Pattern.compile("[0-9]*\\.[0-9]+f?");
36465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern referencePattern = Pattern.compile("\\$[a-zA-Z]+[a-zA-Z0-9]");
36565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        final Pattern booleanPattern = Pattern.compile("true|false");
36665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
36765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        int state = STATE_IDENTIFIER;
36865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        KeyValueMap newVals = new KeyValueMap();
36965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        String curKey = null;
37065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        String curValue = null;
37165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
37265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        while (!scanner.atEnd() && !(endPattern != null && scanner.peek(endPattern))) {
37365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            switch (state) {
37465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_IDENTIFIER:
37565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    curKey = scanner.eat(wordPattern, "<identifier>");
37665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_EQUALS;
37765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
37865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
37965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_EQUALS:
38065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    scanner.eat(equalsPattern, "=");
38165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_VALUE;
38265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
38365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
38465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_VALUE:
38565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    if ((curValue = scanner.tryEat(stringPattern)) != null) {
38665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        newVals.put(curKey, curValue.substring(1, curValue.length() - 1));
38765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if ((curValue = scanner.tryEat(referencePattern)) != null) {
38865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        String refName = curValue.substring(1, curValue.length());
38965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        Object referencedObject = mBoundReferences != null
39065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                            ? mBoundReferences.get(refName)
39165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                            : null;
39265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        if (referencedObject == null) {
39365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                            throw new GraphIOException(
39465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                "Unknown object reference to '" + refName + "'!");
39565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        }
39665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        newVals.put(curKey, referencedObject);
39765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if ((curValue = scanner.tryEat(booleanPattern)) != null) {
39865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        newVals.put(curKey, Boolean.parseBoolean(curValue));
39965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if ((curValue = scanner.tryEat(floatPattern)) != null) {
40065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        newVals.put(curKey, Float.parseFloat(curValue));
40165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else if ((curValue = scanner.tryEat(intPattern)) != null) {
40265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        newVals.put(curKey, Integer.parseInt(curValue));
40365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    } else {
40465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                        throw new GraphIOException(scanner.unexpectedTokenMessage("<value>"));
40565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    }
40665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_POST_VALUE;
40765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
40865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
40965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                case STATE_POST_VALUE:
41065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    scanner.eat(semicolonPattern, ";");
41165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    state = STATE_IDENTIFIER;
41265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    break;
41365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
41465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
41565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
41665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        // Make sure end is expected
41765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (state != STATE_IDENTIFIER && state != STATE_POST_VALUE) {
41865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new GraphIOException(
41965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                "Unexpected end of assignments on line " + scanner.lineNo() + "!");
42065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
42165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
42265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        return newVals;
42365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
42465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
42565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void bindExternal(String name) throws GraphIOException {
42665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (mReferences.containsKey(name)) {
42765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            Object value = mReferences.get(name);
42865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            mBoundReferences.put(name, value);
42965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        } else {
43065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new GraphIOException("Unknown external variable '" + name + "'! "
43165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                + "You must add a reference to this external in the host program using "
43265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                + "addReference(...)!");
43365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
43465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
43565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
43665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    /**
43765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * Unused for now: Often you may want to declare references that are NOT in a certain graph,
43865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * e.g. when reading multiple graphs with the same reader. We could print a warning, but even
43965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     * that may be too much.
44065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn     **/
44165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void checkReferences() throws GraphIOException {
44265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (String reference : mReferences.keySet()) {
44365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (!mBoundReferences.containsKey(reference)) {
44465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new GraphIOException(
44565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    "Host program specifies reference to '" + reference + "', which is not "
44665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    + "declared @external in graph file!");
44765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
44865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
44965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
45065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
45165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void applySettings() throws GraphIOException {
45265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (String setting : mSettings.keySet()) {
45365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            Object value = mSettings.get(setting);
45465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            if (setting.equals("autoBranch")) {
45565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                expectSettingClass(setting, value, String.class);
45665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                if (value.equals("synced")) {
45765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_SYNCED);
45865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                } else if (value.equals("unsynced")) {
45965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_UNSYNCED);
46065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                } else if (value.equals("off")) {
46165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_OFF);
46265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                } else {
46365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                    throw new GraphIOException("Unknown autobranch setting: " + value + "!");
46465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                }
46565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else if (setting.equals("discardUnconnectedOutputs")) {
46665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                expectSettingClass(setting, value, Boolean.class);
46765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                mCurrentGraph.setDiscardUnconnectedOutputs((Boolean)value);
46865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            } else {
46965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                throw new GraphIOException("Unknown @setting '" + setting + "'!");
47065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            }
47165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
47265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
47365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
47465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void expectSettingClass(String setting,
47565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                    Object value,
47665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                                    Class expectedClass) throws GraphIOException {
47765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        if (value.getClass() != expectedClass) {
47865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            throw new GraphIOException("Setting '" + setting + "' must have a value of type "
47965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                + expectedClass.getSimpleName() + ", but found a value of type "
48065953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn                + value.getClass().getSimpleName() + "!");
48165953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
48265953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
48365953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn
48465953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    private void executeCommands() throws GraphIOException {
48565953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        for (Command command : mCommands) {
48665953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn            command.execute(this);
48765953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn        }
48865953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn    }
48965953da4636fbf5f0a24b8f5f2b5fa7d76ff13d9Marius Renn}
490