--- /dev/null Fri Jun 15 22:43:39 2012 +++ new/src/share/classes/org/openjdk/jigsaw/ModuleFileParser.java Fri Jun 15 22:43:39 2012 @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.jigsaw; + +import java.io.InputStream; +import java.util.Iterator; +import java.util.Map.Entry; +import org.openjdk.jigsaw.ModuleFile.ModuleFileHeader; +import org.openjdk.jigsaw.ModuleFile.SectionHeader; +import org.openjdk.jigsaw.ModuleFile.SubSectionFileHeader; + +/** + * The ModuleFileParser interface supports parsing of a + * + * module file. + * + *

The ModuleFileParser is designed to iterate over the module file using + * {@code hasNext()} and {@code next()}. The section type and data can be + * accessed using methods such as {@code getSectionHeader()}, + * {@code getContentStream()}, and {@code getClasses()}.

+ * + *

The {@code next()} method causes the parser to read the next parse event, + * and returns an {@code Event} which identifies the type of event just read. + * The current event type can be determined invoking the {@code event()} + * method.

+ * + *

Convenience methods, such as {@code skipToNextStartSection} and + * {@code skipToNextStartSubSection}, are defined to support easy access to + * (sub)section content without having to handle specific event types.

+ * + *

A minimal example for using this API may look as follows: + *

+ *       try (FileInputStream fis = new FileInputStream(jmodFile)) {
+ *           ModuleFileParser parser = ModuleFile.newParser(fis);
+ *           while (parser.skipToNextStartSection()) {
+ *               SectionHeader header = parser.getSectionHeader();
+ *               SectionType type = header.getType();
+ *               switch (type) {
+ *                   case MODULE_INFO:
+ *                       out.format("%s section, %s%n", type, header); break;
+ *                   case SIGNATURE:
+ *                       out.format("%s section, %s%n", type, header); break;
+ *                   case CLASSES:
+ *                       out.format("%s section, %s%n", type, header); break;
+ *                   case RESOURCES:
+ *                       out.format("%s section, %s%n", type, header); break;
+ *                   case NATIVE_LIBS:
+ *                       out.format("%s section, %s%n", type, header); break;
+ *                   case NATIVE_CMDS:
+ *                       out.format("%s section, %s%n", type, header); break;
+ *                   case CONFIG:
+ *                       out.format("%s section, %s%n", type, header); break;
+ *                   default:
+ *                       throw new IOException("Unknown section type");
+ *               }
+ *           }
+ *       }
+ * 

+ */ + +public interface ModuleFileParser { + + /** + * ModuleFileParser parsing events. + * + *

Events must follow the following grammar. + *

+     *  START_FILE
+     *    START_SECTION          (MODULE_INFO must be present)
+     *    END_SECTION
+     *    [START_SECTION         (optional sections)
+     *       [START_SUBSECTION   (sections with subsections)
+     *        END_SUBSECTION]+
+     *     END_SECTION]+
+     *  END_FILE
+     * 

+ */ + public static enum Event { + /** + * The first event returned. The module header has been consumed. + */ + START_FILE, + /** + * The start of a section. The section header has been consumed. + */ + START_SECTION, + /** + * The start of a subsection. The subsection header has been consumed. + */ + START_SUBSECTION, + /** + * The end of a subsection. The subsection content has been consumed. + */ + END_SUBSECTION, + /** + * The end of a section. The section content has been consumed. + */ + END_SECTION, + /** + * The end of the module file. The file content has been consumed. + */ + END_FILE; + } + + /** + * Returns the module file header. + * + * @return The module file header + */ + public ModuleFileHeader fileHeader(); + + /** + * Returns current event of the parser. + */ + public Event event(); + + /** + * Check if there are more events. + * + * @return true, if and only if, there are more events + */ + public boolean hasNext(); + + /** + * Returns the next parsing event. + * + *

Skips over any unread data from a previous (sub)section.

+ * + * @return the next parse event + * + * @throws NoSuchElementException + * If invoked when {@code hasNext} returns false + * + * @throws ModuleFileParserException + * If there is an error processing the underlying module file + */ + public Event next(); + + /** + * Returns the header of the current section. + * + * @return the section header + * + * @throws ModuleFileParserException + * If the current event is one of START_FILE or END_FILE + */ + public SectionHeader getSectionHeader(); + + /** + * Returns the header of the current file subsection. + * + * @return the subsection header + * + * @throws ModuleFileParserException + * If the current event is not one of START_SUBSECTION or END_SUBSECTION + */ + public SubSectionFileHeader getSubSectionFileHeader(); + + /** + * Returns the hash of the module file header, current section, or file. + * + *

Returns the hash of the module file header if the current event is + * START_FILE, the section hash if the current event is END_SECTION, or the + * complete file contents hash if the current event is END_FILE.

+ * + *

The specific hashes are calculated as per the module-file + * specification. More specifically, the module file header hash and the + * section hash exclude the follow fields from their header, the hash length + * and the hash value. The complete file contents hash excludes the file + * hash value in the module file header and the signature section + * (if present).

+ * + * @return the hash bytes. + * + * @throws ModuleFileParserException + * If the current event is not one of START_FILE, END_SECTION, + * or END_FILE + */ + public byte[] getHash(); + + /** + * Returns an InputStream of the uncompressed content of the current + * section or subsection (if the section defines a compressor), otherwise + * just the raw bytes. + * + *

For the CLASSES section ( {@code getSectionHeader.getType() == + * ModuleFile.SectionType.CLASSES} ) the {@code getClasses} method should be + * invoked. All other sections and subsections can invoke this method to get + * the uncompressed (sub)section content.

+ * + * @return the content stream + * + * @throws ModuleFileParserException + * If the current event is not one of START_SECTION or + * START_SUBSECTION, if {@code getSectionHeader().getType()} returns + * {@code ModuleFile.SectionType.CLASSES}, or if there is an error + * processing the underlying module file + */ + public InputStream getContentStream(); + + /** + * Returns an Iterator over the classes in the CLASSES section. + * + *

If this method is invoked to extract the content of the classes + * section then it must be invoked in a section whose + * {@code getSectionHeader.getType()} returns + * {@code ModuleFile.SectionType.CLASSES}. Any other time will throw a + * {@code ModuleFileParserException}.

+ * + * @return an Iterator over the classes, where the entry key is the class + * file name, e.g. java/lang/Object.class, and the value is an + * input stream containing the class bytes. + * + * @throws ModuleFileParserException + * If the current event is not START_SECTION, if + * {@code getSectionHeader().getType()} does not return + * {@code ModuleFile.SectionType.CLASSES}, or if there is an error + * processing the underlying module file + */ + public Iterator> getClasses(); + + /** + * Returns an InputStream of the raw bytes of the current section or + * subsection. + * + *

If the (sub)section data is compressed then this method simply + * returns the compressed bytes, and the invoker is responsible for + * decompressing.

+ * + * @return The raw (sub)section data + * + * @throws ModuleFileParserException + * If the current event is not one of START_SECTION or START_SUBSECTION + */ + public InputStream getRawStream(); + + /** + * Skips to the start of the next section. + * + *

Skips over any unread data, and consumes events until START_SECTION + * or END_FILE is encountered.

+ * + * @return true, if and only if, the next event is START_SECTION + * + * @throws ModuleFileParserException + * If there is an error processing the underlying module file + */ + public boolean skipToNextStartSection(); + + /** + * Skips to the start of the next subsection. + * + *

Skips over any unread data, and consumes events until END_SECTION + * is encountered.

+ * + * @return true, if and only if, the current event is START_SUBSECTION + * + * @throws ModuleFileParserException + * If not within a section that contains subsections, or if there is + * an error processing the underlying module file + */ + public boolean skipToNextStartSubSection(); +}