1b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato/*
2b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * ProGuard -- shrinking, optimization, obfuscation, and preverification
3b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *             of Java bytecode.
4b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
5b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
6b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
7b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * This program is free software; you can redistribute it and/or modify it
8b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * under the terms of the GNU General Public License as published by the Free
9b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * Software Foundation; either version 2 of the License, or (at your option)
10b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * any later version.
11b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
12b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * This program is distributed in the hope that it will be useful, but WITHOUT
13b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * more details.
16b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
17b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * You should have received a copy of the GNU General Public License along
18b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * with this program; if not, write to the Free Software Foundation, Inc.,
19b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato */
21b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratopackage proguard;
22b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
23b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratoimport java.io.*;
24b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
25b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
26b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato/**
27b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * An abstract reader of words, with the possibility to include other readers.
28b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * Words are separated by spaces or broken off at delimiters. Words containing
29b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * spaces or delimiters can be quoted with single or double quotes.
30b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * Comments (everything starting with '#' on a single line) are ignored.
31b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato *
32b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * @author Eric Lafortune
33b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato * @noinspection TailRecursion
34b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato */
35b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onoratopublic abstract class WordReader
36b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato{
37b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private static final char COMMENT_CHARACTER = '#';
38b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
39b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
40b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private File       baseDir;
41b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private WordReader includeWordReader;
42b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private String     currentLine;
43b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private int        currentLineLength;
44b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private int        currentIndex;
45b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private String     currentWord;
46b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private String     currentComments;
47b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
48b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
49b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
50b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Creates a new WordReader with the given base directory.
51b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
52b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    protected WordReader(File baseDir)
53b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
54b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        this.baseDir = baseDir;
55b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
56b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
57b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
58b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
59b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Sets the base directory of this reader.
60b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
61b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void setBaseDir(File baseDir)
62b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
63b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (includeWordReader != null)
64b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
65b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            includeWordReader.setBaseDir(baseDir);
66b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
67b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        else
68b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
69b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            this.baseDir = baseDir;
70b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
71b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
72b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
73b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
74b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
75b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Returns the base directory of this reader, if any.
76b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
77b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public File getBaseDir()
78b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
79b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        return includeWordReader != null ?
80b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            includeWordReader.getBaseDir() :
81b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            baseDir;
82b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
83b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
84b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
85b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
86b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Specifies to start reading words from the given WordReader. When it is
87b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * exhausted, this WordReader will continue to provide its own words.
88b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     *
89b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * @param newIncludeWordReader the WordReader that will start reading words.
90b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
91b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void includeWordReader(WordReader newIncludeWordReader)
92b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
93b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (includeWordReader == null)
94b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
95b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            includeWordReader = newIncludeWordReader;
96b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
97b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        else
98b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
99b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            includeWordReader.includeWordReader(newIncludeWordReader);
100b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
101b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
102b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
103b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
104b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
105b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Reads a word from this WordReader, or from one of its active included
106b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * WordReader objects.
107b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     *
108b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang     * @param isFileName return a complete line (or argument), if the word
109b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang     *                      isn't an option (it doesn't start with '-').
110b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * @return the read word.
111b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
112b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    public String nextWord(boolean isFileName) throws IOException
113b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
114b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        currentWord = null;
115b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
116b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // See if we have an included reader to produce a word.
117b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (includeWordReader != null)
118b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
119b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Does the included word reader still produce a word?
120b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            currentWord = includeWordReader.nextWord(isFileName);
121b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (currentWord != null)
122b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
123b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                // Return it if so.
124b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                return currentWord;
125b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
126b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
127b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Otherwise close and ditch the word reader.
128b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            includeWordReader.close();
129b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            includeWordReader = null;
130b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
131b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
132b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Get a word from this reader.
133b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
134b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        // Skip any whitespace and comments left on the current line.
135b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        if (currentLine != null)
136b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
137b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // Skip any leading whitespace.
138b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            while (currentIndex < currentLineLength &&
139b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                   Character.isWhitespace(currentLine.charAt(currentIndex)))
140b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            {
141b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                currentIndex++;
142b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            }
143b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
144b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // Skip any comments.
145b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            if (currentIndex < currentLineLength &&
146b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                isComment(currentLine.charAt(currentIndex)))
147b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            {
148b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                currentIndex = currentLineLength;
149b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            }
150b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
151b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
152b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Make sure we have a non-blank line.
153b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        while (currentLine == null || currentIndex == currentLineLength)
154b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
155b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            currentLine = nextLine();
156b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            if (currentLine == null)
157b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
158b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                return null;
159b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
160b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
161b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            currentLineLength = currentLine.length();
162b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
163b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // Skip any leading whitespace.
164b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            currentIndex = 0;
165b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            while (currentIndex < currentLineLength &&
166b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                   Character.isWhitespace(currentLine.charAt(currentIndex)))
167b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
168b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                currentIndex++;
169b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            }
170b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
171b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // Remember any leading comments.
172b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            if (currentIndex < currentLineLength &&
173b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                isComment(currentLine.charAt(currentIndex)))
174b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            {
175b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                // Remember the comments.
176b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                String comment = currentLine.substring(currentIndex + 1);
177b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                currentComments = currentComments == null ?
178b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                    comment :
179b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                    currentComments + '\n' + comment;
180b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
181b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                // Skip the comments.
182b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                currentIndex = currentLineLength;
183b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
184b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
185b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
186b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Find the word starting at the current index.
187b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        int startIndex = currentIndex;
188b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        int endIndex;
189b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
190b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        char startChar = currentLine.charAt(startIndex);
191b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
192b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        if (isQuote(startChar))
193b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
194b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // The next word is starting with a quote character.
195b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Skip the opening quote.
196b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            startIndex++;
197b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
198b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // The next word is a quoted character string.
199b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Find the closing quote.
200b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            do
201b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
202b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                currentIndex++;
203b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
204b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                if (currentIndex == currentLineLength)
205b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                {
206b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                    currentWord = currentLine.substring(startIndex-1, currentIndex);
207b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                    throw new IOException("Missing closing quote for "+locationDescription());
208b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                }
209b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
210b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            while (currentLine.charAt(currentIndex) != startChar);
211b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
212b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            endIndex = currentIndex++;
213b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
214b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        else if (isFileName &&
215b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                 !isOption(startChar))
216b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        {
217b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // The next word is a (possibly optional) file name.
218b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // Find the end of the line, the first path separator, the first
219b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // option, or the first comment.
220b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            while (currentIndex < currentLineLength)
221b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            {
222b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                char currentCharacter = currentLine.charAt(currentIndex);
223b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                if (isFileDelimiter(currentCharacter) ||
224b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                    ((isOption(currentCharacter) ||
225b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                      isComment(currentCharacter)) &&
226b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                     Character.isWhitespace(currentLine.charAt(currentIndex-1)))) {
227b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                    break;
228b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                }
229b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
230b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                currentIndex++;
231b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            }
232b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
233b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            endIndex = currentIndex;
234b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
235b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // Trim any trailing whitespace.
236b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            while (endIndex > startIndex &&
237b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                   Character.isWhitespace(currentLine.charAt(endIndex-1)))
238b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            {
239b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                endIndex--;
240b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            }
241b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        }
242b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        else if (isDelimiter(startChar))
243b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        {
244b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            // The next word is a single delimiting character.
245b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang            endIndex = ++currentIndex;
246b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        }
247b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        else
248b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
249b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // The next word is a simple character string.
250b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // Find the end of the line, the first delimiter, or the first
251b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            // white space.
252b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            while (currentIndex < currentLineLength)
253b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            {
254b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                char currentCharacter = currentLine.charAt(currentIndex);
255b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                if (isDelimiter(currentCharacter)            ||
256b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                    Character.isWhitespace(currentCharacter) ||
257b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang                    isComment(currentCharacter)) {
258b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                    break;
259b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                }
260b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
261b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                currentIndex++;
262b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            }
263b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
264b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            endIndex = currentIndex;
265b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
266b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
267b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Remember and return the parsed word.
268b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        currentWord = currentLine.substring(startIndex, endIndex);
269b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
270b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        return currentWord;
271b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
272b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
273b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
274b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
275b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Returns the comments collected before returning the last word.
276b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Starts collecting new comments.
277b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     *
278b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * @return the collected comments, or <code>null</code> if there weren't any.
279b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
280b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public String lastComments() throws IOException
281b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
282b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (includeWordReader == null)
283b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
284b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            String comments = currentComments;
285b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            currentComments = null;
286b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            return comments;
287b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
288b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        else
289b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
290b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            return includeWordReader.lastComments();
291b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
292b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
293b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
294b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
295b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
296b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Constructs a readable description of the current position in this
297b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * WordReader and its included WordReader objects.
298b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     *
299b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * @return the description.
300b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
301b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public String locationDescription()
302b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
303b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        return
304b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            (includeWordReader == null ?
305b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                (currentWord == null ?
306b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                    "end of " :
307b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                    "'" + currentWord + "' in " ) :
308b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                (includeWordReader.locationDescription() + ",\n" +
309b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato                 "  included from ")) +
310b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            lineLocationDescription();
311b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
312b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
313b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
314b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
315b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Reads a line from this WordReader, or from one of its active included
316b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * WordReader objects.
317b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     *
318b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * @return the read line.
319b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
320b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    protected abstract String nextLine() throws IOException;
321b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
322b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
323b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
324b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Returns a readable description of the current WordReader position.
325b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     *
326b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * @return the description.
327b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
328b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    protected abstract String lineLocationDescription();
329b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
330b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
331b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    /**
332b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     * Closes the FileWordReader.
333b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato     */
334b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    public void close() throws IOException
335b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
336b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        // Close and ditch the included word reader, if any.
337b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        if (includeWordReader != null)
338b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        {
339b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            includeWordReader.close();
340b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato            includeWordReader = null;
341b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        }
342b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
343b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
344b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
345b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    // Small utility methods.
346b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
347b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    private boolean isOption(char character)
348b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    {
349b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        return character == '-';
350b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    }
351b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
352b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
353b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    private boolean isComment(char character)
354b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    {
355b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        return character == COMMENT_CHARACTER;
356b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    }
357b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
358b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
359b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private boolean isDelimiter(char character)
360b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
361b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        return character == '@' ||
362b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato               character == '{' ||
363b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato               character == '}' ||
364b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato               character == '(' ||
365b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato               character == ')' ||
366b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato               character == ',' ||
367b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato               character == ';' ||
368b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato               character == File.pathSeparatorChar;
369b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
370b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
371b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato
372b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    private boolean isFileDelimiter(char character)
373b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    {
374b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang        return character == '(' ||
375b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang               character == ')' ||
376b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang               character == ',' ||
377b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang               character == ';' ||
378b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang               character == File.pathSeparatorChar;
379b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang    }
380b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
381b9cc48a43ed984587c939d02fba5316bf5c0df6eYing Wang
382b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    private boolean isQuote(char character)
383b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    {
384b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato        return character == '\'' ||
385b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato               character == '"';
386b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato    }
387b72c5c2e5482cf10117b2b25f642f7616b2326c3Joe Onorato}
388