| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.openjpa.persistence.jest; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.util.Arrays; |
| |
| /** |
| * Reads from an input stream and writes to an output stream after replacing matched tokens |
| * by their counterpart. |
| * |
| * |
| * @author Pinaki Poddar |
| * |
| */ |
| public class TokenReplacedStream { |
| /** |
| * Read the given input stream and replaces the tokens as it reads. The replaced stream is written to the |
| * given output stream. |
| * |
| * @param in a non-null input stream |
| * @param out a character oriented writer |
| * @param replacements an even number of Strings. Any occurrence of the even-indexed i-th String in the |
| * input stream will be replaced by the (i+1)-th String in the output writer. |
| */ |
| public void replace(InputStream in, Writer out, String... prs) throws IOException { |
| BufferedReader inRdr = new BufferedReader(new InputStreamReader(in)); |
| replace(inRdr, out, prs); |
| } |
| |
| public void replace(Reader in, Writer out, String... prs) throws IOException { |
| if (prs.length%2 != 0) |
| throw new IllegalArgumentException("Even number of pattern/string pairs: " + Arrays.toString(prs) |
| + ". Must be even number of arguments."); |
| Pattern[] patterns = new Pattern[prs.length/2]; |
| for (int i = 0; i < prs.length; i += 2) { |
| patterns[i/2] = new Pattern(prs[i], prs[i+1]); |
| } |
| |
| StringBuilder tmp = new StringBuilder(); |
| for (int c = 0; (c = in.read()) != -1;) { |
| int cursor = match((char)c, patterns); |
| if (cursor < 0) { // no pattern recognized at all |
| if (tmp.length() > 0) { // append partial match then discard partial memory |
| for (int j = 0; j < tmp.length(); j++) { |
| out.write(tmp.charAt(j)); |
| } |
| tmp.delete(0, tmp.length()); |
| } |
| out.write((char)c); // directly output |
| } else { |
| Pattern p = matched(patterns); // has any pattern matched completely |
| if (p != null) { // a pattern matched completely |
| char[] replace = p.replace().toCharArray(); |
| for (char value : replace) { |
| out.write(value); |
| } |
| reset(patterns); |
| tmp.delete(0, tmp.length()); |
| } else { |
| tmp.append((char)c); // remember partial match |
| } |
| } |
| } |
| } |
| |
| /** |
| * Match the given character to all patterns and return the index of highest match. |
| * @param c a character to match |
| * @param patterns an array of patterns |
| * @return -1 if character matched no pattern |
| */ |
| int match(char c, Pattern...patterns) { |
| if (patterns == null) |
| return -1; |
| int result = -1; |
| for (Pattern p : patterns) { |
| result = Math.max(result, p.match(c)); |
| } |
| return result; |
| } |
| |
| /** |
| * Gets the pattern if any in matched state |
| * @param patterns |
| */ |
| Pattern matched(Pattern...patterns) { |
| if (patterns == null) |
| return null; |
| for (Pattern p : patterns) { |
| if (p.isMatched()) return p; |
| } |
| return null; |
| } |
| |
| /** |
| * Resets all the patterns. |
| * @param patterns |
| */ |
| void reset(Pattern...patterns) { |
| if (patterns == null) |
| return; |
| for (Pattern p : patterns) { |
| p.reset(); |
| } |
| } |
| |
| public static class Pattern { |
| private final char[] chars; |
| private final String _replace; |
| private int _cursor; |
| |
| /** |
| * Construct a pattern and its replacement. |
| */ |
| public Pattern(String s, String replace) { |
| if (s == null || s.length() == 0) |
| throw new IllegalArgumentException("Pattern [" + s + "] can not be empty or null "); |
| if (replace == null) |
| throw new IllegalArgumentException("Replacement [" + replace + "] is null for pattern [" + s + "]"); |
| chars = s.toCharArray(); |
| _cursor = -1; |
| _replace = replace; |
| } |
| |
| /** |
| * Match the given character with the current cursor and advance the matching length. |
| * @param c |
| * @return the matching length. -1 denotes the pattern did not match the character. |
| */ |
| public int match(char c) { |
| if (c != chars[++_cursor]) { |
| reset(); |
| } |
| return _cursor; |
| } |
| |
| /** |
| * Reset the cursor. Subsequent matching will begin at start. |
| */ |
| public void reset() { |
| _cursor = -1; |
| } |
| |
| /** |
| * Is this pattern matched fully? |
| * A pattern is fully matched when the matching length is equal to the length of the pattern string. |
| */ |
| public boolean isMatched() { |
| return _cursor == chars.length-1; |
| } |
| |
| /** |
| * Gets the string to be replaced. |
| */ |
| public String replace() { |
| return _replace; |
| } |
| |
| @Override |
| public String toString() { |
| return new String(chars) + ":" + _cursor; |
| } |
| } |
| |
| } |