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