1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.sun.org.apache.xml.internal.resolver.readers; 19 20import com.sun.org.apache.xml.internal.resolver.Catalog; 21import com.sun.org.apache.xml.internal.resolver.CatalogEntry; 22import com.sun.org.apache.xml.internal.resolver.CatalogException; 23import com.sun.org.apache.xml.internal.resolver.readers.CatalogReader; 24import java.io.FileNotFoundException; 25import java.io.IOException; 26import java.io.InputStream; 27import java.net.MalformedURLException; 28import java.net.URL; 29import java.net.URLConnection; 30import java.util.Locale; 31import java.util.Stack; 32import java.util.Vector; 33 34/** 35 * Parses plain text Catalog files. 36 * 37 * <p>This class reads plain text Open Catalog files.</p> 38 * 39 * @see Catalog 40 * 41 * @author Norman Walsh 42 * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a> 43 * 44 */ 45public class TextCatalogReader implements CatalogReader { 46 /** The input stream used to read the catalog */ 47 protected InputStream catfile = null; 48 49 /** 50 * Character lookahead stack. Reading a catalog sometimes requires 51 * up to two characters of lookahead. 52 */ 53 protected int[] stack = new int[3]; 54 55 /** 56 * Token stack. Recognizing an unexpected catalog entry requires 57 * the ability to "push back" a token. 58 */ 59 protected Stack tokenStack = new Stack(); 60 61 /** The current position on the lookahead stack */ 62 protected int top = -1; 63 64 /** Are keywords in the catalog case sensitive? */ 65 protected boolean caseSensitive = false; 66 67 /** 68 * Construct a CatalogReader object. 69 */ 70 public TextCatalogReader() { } 71 72 public void setCaseSensitive(boolean isCaseSensitive) { 73 caseSensitive = isCaseSensitive; 74 } 75 76 public boolean getCaseSensitive() { 77 return caseSensitive; 78 } 79 80 /** 81 * Start parsing a text catalog file. The file is 82 * actually read and parsed 83 * as needed by <code>nextEntry</code>.</p> 84 * 85 * @param fileUrl The URL or filename of the catalog file to process 86 * 87 * @throws MalformedURLException Improper fileUrl 88 * @throws IOException Error reading catalog file 89 */ 90 public void readCatalog(Catalog catalog, String fileUrl) 91 throws MalformedURLException, IOException { 92 URL catURL = null; 93 94 try { 95 catURL = new URL(fileUrl); 96 } catch (MalformedURLException e) { 97 catURL = new URL("file:///" + fileUrl); 98 } 99 100 URLConnection urlCon = catURL.openConnection(); 101 try { 102 readCatalog(catalog, urlCon.getInputStream()); 103 } catch (FileNotFoundException e) { 104 catalog.getCatalogManager().debug.message(1, "Failed to load catalog, file not found", 105 catURL.toString()); 106 } 107 } 108 109 public void readCatalog(Catalog catalog, InputStream is) 110 throws MalformedURLException, IOException { 111 112 catfile = is; 113 114 if (catfile == null) { 115 return; 116 } 117 118 Vector unknownEntry = null; 119 120 try { 121 while (true) { 122 String token = nextToken(); 123 124 if (token == null) { 125 if (unknownEntry != null) { 126 catalog.unknownEntry(unknownEntry); 127 unknownEntry = null; 128 } 129 catfile.close(); 130 catfile = null; 131 return; 132 } 133 134 String entryToken = null; 135 if (caseSensitive) { 136 entryToken = token; 137 } else { 138 entryToken = token.toUpperCase(Locale.ENGLISH); 139 } 140 141 try { 142 int type = CatalogEntry.getEntryType(entryToken); 143 int numArgs = CatalogEntry.getEntryArgCount(type); 144 Vector args = new Vector(); 145 146 if (unknownEntry != null) { 147 catalog.unknownEntry(unknownEntry); 148 unknownEntry = null; 149 } 150 151 for (int count = 0; count < numArgs; count++) { 152 args.addElement(nextToken()); 153 } 154 155 catalog.addEntry(new CatalogEntry(entryToken, args)); 156 } catch (CatalogException cex) { 157 if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { 158 if (unknownEntry == null) { 159 unknownEntry = new Vector(); 160 } 161 unknownEntry.addElement(token); 162 } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { 163 catalog.getCatalogManager().debug.message(1, "Invalid catalog entry", token); 164 unknownEntry = null; 165 } else if (cex.getExceptionType() == CatalogException.UNENDED_COMMENT) { 166 catalog.getCatalogManager().debug.message(1, cex.getMessage()); 167 } 168 } 169 } 170 } catch (CatalogException cex2) { 171 if (cex2.getExceptionType() == CatalogException.UNENDED_COMMENT) { 172 catalog.getCatalogManager().debug.message(1, cex2.getMessage()); 173 } 174 } 175 } 176 177 /** 178 * The destructor. 179 * 180 * <p>Makes sure the catalog file is closed.</p> 181 */ 182 protected void finalize() { 183 if (catfile != null) { 184 try { 185 catfile.close(); 186 } catch (IOException e) { 187 // whatever... 188 } 189 } 190 catfile = null; 191 } 192 193 // ----------------------------------------------------------------- 194 195 /** 196 * Return the next token in the catalog file. 197 * 198 * <p>FYI: This code does not throw any sort of exception for 199 * a file that contains an n 200 * 201 * @return The Catalog file token from the input stream. 202 * @throws IOException If an error occurs reading from the stream. 203 */ 204 protected String nextToken() throws IOException, CatalogException { 205 String token = ""; 206 int ch, nextch; 207 208 if (!tokenStack.empty()) { 209 return (String) tokenStack.pop(); 210 } 211 212 // Skip over leading whitespace and comments 213 while (true) { 214 // skip leading whitespace 215 ch = catfile.read(); 216 while (ch <= ' ') { // all ctrls are whitespace 217 ch = catfile.read(); 218 if (ch < 0) { 219 return null; 220 } 221 } 222 223 // now 'ch' is the current char from the file 224 nextch = catfile.read(); 225 if (nextch < 0) { 226 return null; 227 } 228 229 if (ch == '-' && nextch == '-') { 230 // we've found a comment, skip it... 231 ch = ' '; 232 nextch = nextChar(); 233 while ((ch != '-' || nextch != '-') && nextch > 0) { 234 ch = nextch; 235 nextch = nextChar(); 236 } 237 238 if (nextch < 0) { 239 throw new CatalogException(CatalogException.UNENDED_COMMENT, 240 "Unterminated comment in catalog file; EOF treated as end-of-comment."); 241 } 242 243 // Ok, we've found the end of the comment, 244 // loop back to the top and start again... 245 } else { 246 stack[++top] = nextch; 247 stack[++top] = ch; 248 break; 249 } 250 } 251 252 ch = nextChar(); 253 if (ch == '"' || ch == '\'') { 254 int quote = ch; 255 while ((ch = nextChar()) != quote) { 256 char[] chararr = new char[1]; 257 chararr[0] = (char) ch; 258 String s = new String(chararr); 259 token = token.concat(s); 260 } 261 return token; 262 } else { 263 // return the next whitespace or comment delimited 264 // string 265 while (ch > ' ') { 266 nextch = nextChar(); 267 if (ch == '-' && nextch == '-') { 268 stack[++top] = ch; 269 stack[++top] = nextch; 270 return token; 271 } else { 272 char[] chararr = new char[1]; 273 chararr[0] = (char) ch; 274 String s = new String(chararr); 275 token = token.concat(s); 276 ch = nextch; 277 } 278 } 279 return token; 280 } 281 } 282 283 /** 284 * Return the next logical character from the input stream. 285 * 286 * @return The next (logical) character from the input stream. The 287 * character may be buffered from a previous lookahead. 288 * 289 * @throws IOException If an error occurs reading from the stream. 290 */ 291 protected int nextChar() throws IOException { 292 if (top < 0) { 293 return catfile.read(); 294 } else { 295 return stack[top--]; 296 } 297 } 298} 299