/* * * 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.flex.compiler.internal.parsing; import java.io.IOException; import java.io.Reader; import org.apache.flex.utils.NonLockingStringReader; /** * Reader that spoofs adding several characters to the end of the existing Reader. Reading from the * FakingReader returns characters from the original Reader until they run out, at which point it returns * characters from its buffer of fake characters */ public class FakingReader extends Reader { /** * Constructor * @param reader the original reader * @param fakeCharacters the fake characters to "append" to the end */ public FakingReader(Reader reader, String fakeCharacters) { super(); this.reader = reader; fakeReader = new NonLockingStringReader(fakeCharacters); readingFakeCharacters = false; // See the comments on mungeDifficultCharacters(), below // fMungeDifficultCharacters = mungeDifficultCharacters; // fOffset = 0; // fDifficultCharacters = new ArrayList(); // fDifficultCharacterOffsets = new ArrayList(); } /** * The original Reader that contains the real characters */ protected Reader reader; /** * The StringReader that contains the fake characters */ protected NonLockingStringReader fakeReader; /** * Flag indicating which reader is currently in use */ protected boolean readingFakeCharacters; /** * @see java.io.Reader#close() */ @Override public void close() throws IOException { reader.close(); } // /** // * Flag indicating that we should munge difficult characters to prepare for JLex // */ // protected boolean fMungeDifficultCharacters; // /** // * Current offset into the stream // */ // protected int fOffset; // /** // * List of difficult characters that we've had to munge before sending to JLex // */ // protected ArrayList fDifficultCharacters; // /** // * List of offsets at which we've had to munge characters before sending to JLex // */ // protected ArrayList fDifficultCharacterOffsets; // /** // * Locate any identifier-legal characters that we know we'll have trouble // * identifying in the tokenizer, and replace them with simpler characters. // * This is designed to be called after reading characters (from the real // * stream) and read() // * // * @param cbuf buffer of characters just filled in read() // * @param off the offset at which read() just put the characters // * @param len the number of character successfully read // */ // protected void mungeDifficultCharacters(char[] cbuf, int off, int len) // { // // The following are all legal identifier characters in ECMAScript // // Uppercase letter (Lu) // // Lowercase letter (Ll) // // Titlecase letter (Lt) // // Modifier letter (Lm) // // Other letter (Lo) // // Letter number (Nl) * // // $ and _ // // Unicode escape sequence (\uFFFF) // // Non-spacing mark (Mn) * // // Combining spacing mark (Mc) * // // Decimal number (Nd) // // Connector punctuation (Pc) * // // // // In JLex, we can use the builtin character classes // // [:letter:] = Character.isLetter() = Lu + Ll + Lt + Lm + Lo // // [:digit:] = Character.isDigit() = Nd // // and we can use simple regular expressions to handle $ and _ // // and the Unicode escape sequence (\uFFFF). The other classes // // (Nl, Mn, Mc, Nd, Pc) are not easy to identify in JLex, since there's // // no generic character class support. We'll identify the characters // // here and replace them with something more easily managed, then we'll // // unmunge the tokens in ASTokenizer before we do anything else with them. // // for (int i = off; i < off + len; i++) // { // if (Character.getType(cbuf[i]) == Character.LETTER_NUMBER) // { // // This is a valid starting character for an identifier, so // // "a" is a suitable equivalent // fDifficultCharacterOffsets.add(new Integer(fOffset + i - off)); // fDifficultCharacters.add(new Character(cbuf[i])); // cbuf[i] = 'a'; // } // else if (cbuf[i] != '_' && // (Character.getType(cbuf[i]) == Character.NON_SPACING_MARK || // Character.getType(cbuf[i]) == Character.COMBINING_SPACING_MARK || // Character.getType(cbuf[i]) == Character.CONNECTOR_PUNCTUATION)) // { // // This is not a valid starting character for an identifier, but // // is a valid following character for an identifier, so "0" is a // // suitable equivalent // fDifficultCharacterOffsets.add(new Integer(fOffset + i - off)); // fDifficultCharacters.add(new Character(cbuf[i])); // cbuf[i] = '0'; // } // } // } // /** // * Undo any character transformation that we accomplished in mungeDifficultCharacters // * @param tokenString the token string that came back from the tokenizer // * (possibly containing transformed characters) // * @param startOffset the start offset of that string // * @return the same string, with the original characters put back // */ // public String unmungeDifficultCharacters(String tokenString, int startOffset) // { // int endOffset = startOffset + tokenString.length(); // char [] tokenChars = null; // for (int i = 0; i < fDifficultCharacterOffsets.size(); i++) // { // int difficultCharacterOffset = ((Integer)fDifficultCharacterOffsets.get(i)).intValue(); // if (difficultCharacterOffset >= startOffset && // difficultCharacterOffset <= endOffset) // { // if (tokenChars == null) // tokenChars = tokenString.toCharArray(); // tokenChars[difficultCharacterOffset - startOffset] = // ((Character)fDifficultCharacters.get(i)).charValue(); // } // else if (difficultCharacterOffset > endOffset) // { // break; // } // } // if (tokenChars != null) // tokenString = new String(tokenChars); // return tokenString; // } /** * @see java.io.Reader#read(char[], int, int) */ @Override public int read(char[] cbuf, int off, int len) throws IOException { if (readingFakeCharacters) { // We're already past the end of the real stream and in the fake characters return fakeReader.read(cbuf, off, len); } int charactersRead = reader.read(cbuf, off, len); if (charactersRead == -1) { // We're past the end of the real stream and in the fake characters readingFakeCharacters = true; return fakeReader.read(cbuf, off, len); } // See the comments on mungeDifficultCharacters(), above // if (fMungeDifficultCharacters) // { // mungeDifficultCharacters(cbuf, off, charactersRead); // fOffset += charactersRead; // } // We're still reading characters from the real stream. return charactersRead; } }