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 */ 021 022package org.biojava.bio.program.tagvalue; 023 024import java.io.BufferedReader; 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.Iterator; 028import java.util.List; 029 030import org.biojava.utils.ParserException; 031 032/** 033 * <p> 034 * Encapsulate the parsing of lines from a buffered reader into tag-value 035 * events. 036 * </p> 037 * 038 * <p> 039 * Scripts will usually construct a parser object, a BufferedReader, a 040 * TagValueParser and TagValueListener, and then set up a loop that processes 041 * each record in the reader by calling Parser.read() until it returns false. 042 * </p> 043 * 044 * @since 1.2 045 * @author Matthew Pocock 046 */ 047public class Parser { 048 public boolean read( 049 BufferedReader reader, 050 TagValueParser parser, 051 TagValueListener listener 052 ) throws 053 IOException, 054 ParserException 055 { 056 final List stack = new ArrayList(); 057 push(stack, new Frame(parser, listener, null)); 058 059 Context ctxt = new Context(); 060 061 listener.startRecord(); 062 063 for( 064 Object line = reader.readLine(); 065 line != null; 066 line = reader.readLine() 067 ) { 068 // Find the deepest stack-frame with the same key. 069 // If this is not the deepest one, unwind the stack to that point 070 Frame frame = null; 071 TagValue tv = null; 072 for(Iterator fi = stack.iterator(); fi.hasNext(); ) { 073 frame = (Frame) fi.next(); 074 075 tv = frame.parser.parse(line); 076 077 // end of record. Is it the last in the file? 078 if(tv == null) { 079 // scan for eof after whitespace 080 boolean eof = false; 081 while(true) { 082 reader.mark(1); 083 int c = reader.read(); 084 if(c == -1) { 085 eof = true; 086 break; 087 } 088 089 if(Character.isWhitespace((char) c)) { 090 continue; 091 } 092 093 reader.reset(); 094 break; 095 } 096 097 // now unwind stack 098 do { 099 Frame top = (Frame) pop(stack); 100 top.listener.endTag(); 101 top.listener.endRecord(); 102 } while(!stack.isEmpty()); 103 return !eof; 104 } 105 106 // not a continuation of a previous tag - unwrap stack 107 if( 108 tv.isNewTag() || 109 (tv.getTag() != null && !tv.getTag().equals(frame.tag)) 110 ) { 111 // remove all stack frames which have been obsoleted by this tag 112 Frame top; 113 for(top = (Frame) pop(stack); top != frame; top = (Frame) pop(stack)) { 114 top.listener.endTag(); 115 top.listener.endRecord(); 116 } 117 if(top.tag != null) { 118 top.listener.endTag(); 119 } 120 121 // handle current stack frame by starting a tag 122 push(stack, new Frame(top.parser, top.listener, tv.getTag())); 123 top.listener.startTag(tv.getTag()); 124 break; 125 } 126 127 line = tv.getValue(); 128 } 129 130 // process a value and handle potentially pushing a new stack frame 131 while(true) { 132 // pass in value and see if it requests a new stack frame 133 ctxt.flush(); 134 frame.listener.value(ctxt, tv.getValue()); 135 if(!ctxt.isDirty()) { 136 break; 137 } 138 139 // push a new stack frame 140 tv = ctxt.parser.parse(tv.getValue()); 141 frame = new Frame(ctxt.parser, ctxt.listener, tv.getTag()); 142 push(stack, frame); 143 ctxt.listener.startRecord(); 144 ctxt.listener.startTag(tv.getTag()); 145 146 // we must loop arround in case the new frame wants to immediately push a 147 // new stack frame 148 } 149 } 150 151 throw new IOException("Premature end of stream or missing end tag"); 152 } 153 154 private void push(List stack, Object o) { 155 stack.add(o); 156 } 157 158 private Object pop(List stack) { 159 return stack.remove(stack.size() - 1); 160 } 161 162 private static class Frame { 163 public final TagValueParser parser; 164 public final TagValueListener listener; 165 public final Object tag; 166 167 public Frame( 168 TagValueParser parser, 169 TagValueListener listener, 170 Object tag 171 ) { 172 this.parser = parser; 173 this.listener = listener; 174 this.tag = tag; 175 } 176 } 177 178 private static class Context 179 implements 180 TagValueContext 181 { 182 public TagValueParser parser; 183 public TagValueListener listener; 184 185 public void pushParser( 186 TagValueParser subParser, 187 TagValueListener listener 188 ) { 189 this.parser = subParser; 190 this.listener = listener; 191 } 192 193 public void flush() { 194 this.parser = null; 195 this.listener = null; 196 } 197 198 public boolean isDirty() { 199 return parser != null; 200 } 201 } 202}