1 /*
   2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package org.openjdk.jigsaw;
  27 
  28 import java.io.InputStream;
  29 import java.util.Iterator;
  30 import java.util.Map.Entry;
  31 import org.openjdk.jigsaw.ModuleFile.ModuleFileHeader;
  32 import org.openjdk.jigsaw.ModuleFile.SectionHeader;
  33 import org.openjdk.jigsaw.ModuleFile.SubSectionFileHeader;
  34 
  35 /**
  36  * The ModuleFileParser interface supports parsing of a
  37  * <a href="http://cr.openjdk.java.net/~mr/jigsaw/notes/module-file-format/">
  38  * <i>module file</i></a>.
  39  *
  40  * <p> The ModuleFileParser is designed to iterate over the module file using
  41  * {@code hasNext()} and {@code next()}. The section type and data can be
  42  * accessed using methods such as {@code getSectionHeader()},
  43  * {@code getContentStream()}, and {@code getClasses()}. </p>
  44  *
  45  * <p> The {@code next()} method causes the parser to read the next parse event,
  46  * and returns an {@code Event} which identifies the type of event just read.
  47  * The current event type can be determined invoking the {@code event()}
  48  * method. </p>
  49  *
  50  * <p> Convenience methods, such as {@code skipToNextStartSection} and
  51  * {@code skipToNextStartSubSection}, are defined to support easy access to
  52  * (sub)section content without having to handle specific event types. </p>
  53  *
  54  * <p> A minimal example for using this API may look as follows:
  55  * <pre>
  56  *       try (FileInputStream fis = new FileInputStream(jmodFile)) {
  57  *           ModuleFileParser parser = ModuleFile.newParser(fis);
  58  *           while (parser.skipToNextStartSection()) {
  59  *               SectionHeader header = parser.getSectionHeader();
  60  *               SectionType type = header.getType();
  61  *               switch (type) {
  62  *                   case MODULE_INFO:
  63  *                       out.format("%s section, %s%n", type, header); break;
  64  *                   case SIGNATURE:
  65  *                       out.format("%s section, %s%n", type, header); break;
  66  *                   case CLASSES:
  67  *                       out.format("%s section, %s%n", type, header); break;
  68  *                   case RESOURCES:
  69  *                       out.format("%s section, %s%n", type, header); break;
  70  *                   case NATIVE_LIBS:
  71  *                       out.format("%s section, %s%n", type, header); break;
  72  *                   case NATIVE_CMDS:
  73  *                       out.format("%s section, %s%n", type, header); break;
  74  *                   case CONFIG:
  75  *                       out.format("%s section, %s%n", type, header); break;
  76  *                   default:
  77  *                       throw new IOException("Unknown section type");
  78  *               }
  79  *           }
  80  *       }
  81  * </pre></p>
  82  */
  83 
  84 public interface ModuleFileParser {
  85 
  86     /**
  87      * ModuleFileParser parsing events.
  88      *
  89      * <p> Events must follow the following grammar.
  90      * <pre>
  91      *  START_FILE
  92      *    START_SECTION          (MODULE_INFO must be present)
  93      *    END_SECTION
  94      *    [START_SECTION         (optional sections)
  95      *       [START_SUBSECTION   (sections with subsections)
  96      *        END_SUBSECTION]+
  97      *     END_SECTION]+
  98      *  END_FILE
  99      * </pre> </p>
 100      */
 101     public static enum Event {
 102         /**
 103          * The first event returned. The module header has been consumed.
 104          */
 105         START_FILE,
 106         /**
 107          * The start of a section. The section header has been consumed.
 108          */
 109         START_SECTION,
 110         /**
 111          * The start of a subsection. The subsection header has been consumed.
 112          */
 113         START_SUBSECTION,
 114         /**
 115          * The end of a subsection. The subsection content has been consumed.
 116          */
 117         END_SUBSECTION,
 118         /**
 119          * The end of a section. The section content has been consumed.
 120          */
 121         END_SECTION,
 122         /**
 123          * The end of the module file. The file content has been consumed.
 124          */
 125         END_FILE;
 126     }
 127 
 128     /**
 129      * Returns the module file header.
 130      *
 131      * @return  The module file header
 132      */
 133     public ModuleFileHeader fileHeader();
 134 
 135     /**
 136      * Returns current event of the parser.
 137      */
 138     public Event event();
 139 
 140     /**
 141      * Check if there are more events.
 142      *
 143      * @return  true, if and only if, there are more events
 144      */
 145     public boolean hasNext();
 146 
 147     /**
 148      * Returns the next parsing event.
 149      *
 150      * <p> Skips over any unread data from a previous (sub)section. </p>
 151      *
 152      * @return  the next parse event
 153      *
 154      * @throws NoSuchElementException
 155      *         If invoked when {@code hasNext} returns false
 156      *
 157      * @throws ModuleFileParserException
 158      *         If there is an error processing the underlying module file
 159      */
 160     public Event next();
 161 
 162     /**
 163      * Returns the header of the current section.
 164      *
 165      * @return  the section header
 166      *
 167      * @throws ModuleFileParserException
 168      *         If the current event is one of START_FILE or END_FILE
 169      */
 170     public SectionHeader getSectionHeader();
 171 
 172     /**
 173      * Returns the header of the current file subsection.
 174      *
 175      * @return  the subsection header
 176      *
 177      * @throws ModuleFileParserException
 178      *         If the current event is not one of START_SUBSECTION or END_SUBSECTION
 179      */
 180     public SubSectionFileHeader getSubSectionFileHeader();
 181 
 182     /**
 183      * Returns the hash of the module file header, current section, or file.
 184      *
 185      * <p> Returns the hash of the module file header if the current event is
 186      * START_FILE, the section hash if the current event is END_SECTION, or the
 187      * complete file contents hash if the current event is END_FILE. </p>
 188      *
 189      * <p> The specific hashes are calculated as per the module-file
 190      * specification. More specifically, the module file header hash and the
 191      * section hash exclude the follow fields from their header, the hash length
 192      * and the hash value. The complete file contents hash excludes the file
 193      * hash value in the module file header and the signature section
 194      * (if present). </p>
 195      *
 196      * @return  the hash bytes.
 197      *
 198      * @throws ModuleFileParserException
 199      *         If the current event is not one of START_FILE, END_SECTION,
 200      *         or END_FILE
 201      */
 202     public byte[] getHash();
 203 
 204     /**
 205      * Returns an InputStream of the uncompressed content of the current
 206      * section or subsection (if the section defines a compressor), otherwise
 207      * just the raw bytes.
 208      *
 209      * <p> For the CLASSES section ( {@code getSectionHeader.getType() ==
 210      * ModuleFile.SectionType.CLASSES} ) the {@code getClasses} method should be
 211      * invoked. All other sections and subsections can invoke this method to get
 212      * the uncompressed (sub)section content. </p>
 213      *
 214      * @return  the content stream
 215      *
 216      * @throws ModuleFileParserException
 217      *         If the current event is not one of START_SECTION or
 218      *         START_SUBSECTION, if {@code getSectionHeader().getType()} returns
 219      *         {@code ModuleFile.SectionType.CLASSES}, or if there is an error
 220      *         processing the underlying module file
 221      */
 222     public InputStream getContentStream();
 223 
 224     /**
 225      * Returns an Iterator over the classes in the CLASSES section.
 226      *
 227      * <p> If this method is invoked to extract the content of the classes
 228      * section then it must be invoked in a section whose
 229      * {@code getSectionHeader.getType()} returns
 230      * {@code ModuleFile.SectionType.CLASSES}. Any other time will throw a
 231      * {@code ModuleFileParserException}. </p>
 232      *
 233      * @return  an Iterator over the classes, where the entry key is the class
 234      *          file name, e.g. java/lang/Object.class, and the value is an
 235      *          input stream containing the class bytes.
 236      *
 237      * @throws ModuleFileParserException
 238      *         If the current event is not START_SECTION, if
 239      *         {@code getSectionHeader().getType()} does not return
 240      *         {@code ModuleFile.SectionType.CLASSES}, or if there is an error
 241      *         processing the underlying module file
 242      */
 243     public Iterator<Entry<String,InputStream>> getClasses();
 244 
 245     /**
 246      * Returns an InputStream of the raw bytes of the current section or
 247      * subsection.
 248      *
 249      * <p> If the (sub)section data is compressed then this method simply
 250      * returns the compressed bytes, and the invoker is responsible for
 251      * decompressing. </p>
 252      *
 253      * @return  The raw (sub)section data
 254      *
 255      * @throws ModuleFileParserException
 256      *         If the current event is not one of START_SECTION or START_SUBSECTION
 257      */
 258     public InputStream getRawStream();
 259 
 260     /**
 261      * Skips to the start of the next section.
 262      *
 263      * <p> Skips over any unread data, and consumes events until START_SECTION
 264      * or END_FILE is encountered. </p>
 265      *
 266      * @return  true, if and only if, the next event is START_SECTION
 267      *
 268      * @throws ModuleFileParserException
 269      *         If there is an error processing the underlying module file
 270      */
 271     public boolean skipToNextStartSection();
 272 
 273     /**
 274      * Skips to the start of the next subsection.
 275      *
 276      * <p> Skips over any unread data, and consumes events until END_SECTION
 277      * is encountered. </p>
 278      *
 279      * @return  true, if and only if, the current event is START_SUBSECTION
 280      *
 281      * @throws ModuleFileParserException
 282      *         If not within a section that contains subsections, or if there is
 283      *         an error processing the underlying module file
 284      */
 285     public boolean skipToNextStartSubSection();
 286 }