001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojavax.bio.phylo.io.nexus;
022
023import java.util.HashMap;
024import java.util.Map;
025
026import org.biojava.bio.seq.io.ParseException;
027
028/**
029 * Listens to events fired by the Nexus parser. Use these events to handle data
030 * directly or construct objects.
031 * 
032 * @author Richard Holland
033 * @author Tobias Thierer
034 * @author Jim Balhoff
035 * @since 1.6
036 */
037public interface NexusFileListener {
038
039        /**
040         * About to start a new file.
041         */
042        public void startFile();
043
044        /**
045         * Finished reading a file.
046         */
047        public void endFile();
048
049        /**
050         * Opening a comment tag.
051         */
052        public void beginComment();
053
054        /**
055         * Receiving free text inside a comment tag.
056         * 
057         * @param comment
058         *            the text of the comment.
059         */
060        public void commentText(String comment) throws ParseException;
061
062        /**
063         * Closing a comment tag.
064         */
065        public void endComment();
066
067        /**
068         * Closing a line (semi-colon encountered). This indicates that anything
069         * received after it is on the next logical line of the file.
070         */
071        public void endTokenGroup();
072
073        /**
074         * Causes the default block parsers to be assigned. This is called by the
075         * constructor of the abstract implementation. If it is not called, then at
076         * least the unknown block parser must be set by other means.
077         */
078        public void setDefaultBlockParsers();
079
080        /**
081         * Sets the parser to use for a given block.
082         * 
083         * @param blockName
084         *            the name of the block.
085         * @param parser
086         *            the parser to use. Use <tt>null</tt> to unset an existing
087         *            one and use the default one for that block instead.
088         */
089        public void setBlockParser(String blockName, NexusBlockParser parser);
090
091        /**
092         * Gets the parser to use for a given block.
093         * 
094         * @param blockName
095         *            the name of the block. return parser the parser to use. Is
096         *            never null.
097         */
098        public NexusBlockParser getBlockParser(String blockName);
099
100        /**
101         * About to start a new block.
102         * 
103         * @param blockName
104         *            the name of the new block.
105         */
106        public void startBlock(String blockName);
107
108        /**
109         * Finished reading a block.
110         */
111        public void endBlock();
112
113        /**
114         * Encountered a token.
115         * 
116         * @param token
117         *            the token.
118         * @throws ParseException
119         *             if the token is invalid.
120         */
121        public void parseToken(String token) throws ParseException;
122
123        /**
124         * Does the listener want to know about brackets and braces as separate
125         * tokens?
126         * 
127         * @return <tt>true</tt> if it does.
128         */
129        public boolean wantsBracketsAndBraces();
130
131        /**
132         * Example abstract implementation which all others should extend.
133         */
134        public abstract class Abstract implements NexusFileListener {
135
136                private static final NexusBlockParser ignoreUnknownBlocks = new NexusBlockParser.Abstract(
137                                new NexusBlockListener() {
138                                        public void beginComment() {
139                                        }
140
141                                        public void commentText(String comment)
142                                                        throws ParseException {
143                                        }
144
145                                        public void endBlock() {
146                                        }
147
148                                        public void endComment() {
149                                        }
150
151                                        public void endTokenGroup() {
152                                        }
153
154                                        public void startBlock(final String blockName) {
155                                        }
156                                }) {
157                        public void resetStatus() {
158                        }
159
160                        public void parseToken(final String token) throws ParseException {
161                        }
162                };
163
164                private Map blockParsers = new HashMap();
165
166                private NexusBlockParser blockParser;
167
168                public Abstract() {
169                        this.setDefaultBlockParsers();
170                }
171
172                public void beginComment() {
173                        if (this.blockParser != null)
174                                this.blockParser.beginComment();
175                        else
176                                this.beginFileComment();
177                }
178
179                /**
180                 * This method will get called when a comment is started on the file,
181                 * and not any block within it.
182                 */
183                protected abstract void beginFileComment();
184
185                public void commentText(String comment) throws ParseException {
186                        if (this.blockParser != null)
187                                this.blockParser.commentText(comment);
188                        else
189                                this.fileCommentText(comment);
190                }
191
192                /**
193                 * This method will get called when comment text is found on the file,
194                 * and not any block within it.
195                 * 
196                 * @param comment
197                 *            the comment text.
198                 */
199                protected abstract void fileCommentText(String comment);
200
201                public void endComment() {
202                        if (this.blockParser != null)
203                                this.blockParser.endComment();
204                        else
205                                this.endFileComment();
206                }
207
208                /**
209                 * This method will get called when a comment is ended on the file, and
210                 * not any block within it.
211                 */
212                protected abstract void endFileComment();
213
214                public void endBlock() {
215                        this.blockParser.endBlock();
216                        this.blockEnded(this.blockParser);
217                        this.blockParser = null;
218                }
219
220                /**
221                 * This method gets called when the block parser is expected to have
222                 * finished parsing a block.
223                 * 
224                 * @param blockParser
225                 *            the parser that has finished.
226                 */
227                protected abstract void blockEnded(NexusBlockParser blockParser);
228
229                public boolean wantsBracketsAndBraces() {
230                        return this.blockParser != null
231                                        && this.blockParser.wantsBracketsAndBraces();
232                }
233
234                public void setDefaultBlockParsers() {
235                        this.setBlockParser(NexusBlockParser.UNKNOWN_BLOCK,
236                                        NexusFileListener.Abstract.ignoreUnknownBlocks);
237                }
238
239                public NexusBlockParser getBlockParser(String blockName) {
240                        blockName = blockName.toUpperCase();
241                        return blockParser = this.blockParsers.containsKey(blockName) ? (NexusBlockParser) this.blockParsers
242                                        .get(blockName)
243                                        : (NexusBlockParser) this.blockParsers
244                                                        .get(NexusBlockParser.UNKNOWN_BLOCK);
245                }
246
247                public void endTokenGroup() {
248                        // Only blocks care about semi-colons.
249                        if (this.blockParser != null)
250                                this.blockParser.endTokenGroup();
251                }
252
253                public void parseToken(String token) throws ParseException {
254                        // Only blocks can parse tokens.
255                        if (this.blockParser != null)
256                                this.blockParser.parseToken(token);
257                }
258
259                public void setBlockParser(String blockName, NexusBlockParser parser) {
260                        this.blockParsers.put(blockName.toUpperCase(), parser);
261                }
262
263                public void startBlock(String blockName) {
264                        this.blockParser = this.getBlockParser(blockName);
265                        this.blockParser.startBlock(blockName);
266                }
267
268        }
269}