SquirrelJME/SquirrelJME

View on GitHub
modules/meep-midlet/src/main/java/cc/squirreljme/runtime/midlet/ManifestSourceType.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.runtime.midlet;

import cc.squirreljme.jvm.mle.JarPackageShelf;
import cc.squirreljme.jvm.mle.RawJarPackageBracketInputStream;
import cc.squirreljme.jvm.mle.brackets.JarPackageBracket;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import javax.microedition.midlet.MIDlet;
import net.multiphasicapps.collections.UnmodifiableCollection;

/**
 * Represents a source type for a given manifest for any given {@link MIDlet}.
 *
 * @since 2022/03/04
 */
public enum ManifestSourceType
{
    /** JAD Manifest. */
    JAD
    {
        /**
         * {@inheritDoc}
         * @since 2022/03/04
         */
        @Override
        public InputStream manifestStream(JarPackageBracket __ourJar)
            throws IOException, NullPointerException
        {
            if (__ourJar == null)
                throw new NullPointerException("NARG");
            
            // Make assumptions on what our JAD is called.
            String ourPath = JarPackageShelf.libraryPath(__ourJar);
            String assumedJad;
            if (ourPath.endsWith(".jar") || ourPath.endsWith(".JAR"))
            {
                boolean caps = ourPath.endsWith(".JAR");
                
                assumedJad = ourPath.substring(0,
                    ourPath.length() - 4) + (caps ? ".JAD" : ".jad");
            }
            else
                assumedJad = ourPath + ".jad";
            
            // Find the matching assumed JAD
            for (JarPackageBracket otherJar : JarPackageShelf.libraries())
            {
                // If the other JAR is a match for the path we assumed, then
                // we should attempt reading the JAD to get properties from
                String otherPath = JarPackageShelf.libraryPath(otherJar);
                if (assumedJad.equals(otherPath))
                    return JarPackageShelf.openResource(otherJar,
                    "META-INF/MANIFEST.MF");
                
            }
            
            // Not found
            return null;
        }
    },
    
    /** KJX Embedded JAD manifest. */
    KJX_EMBEDDED_JAD
    {
        /**
         * {@inheritDoc}
         * @since 2022/03/04
         */
        @Override
        public String encoding()
        {
            // For some reason, Shift-JIS is used for the manifests instead
            // of what should be UTF-8...
            return "shift-jis";
        }
        
        /**
         * {@inheritDoc}
         * @since 2022/03/04
         */
        @Override
        public InputStream manifestStream(JarPackageBracket __ourJar)
            throws IOException, NullPointerException
        {
            if (__ourJar == null)
                throw new NullPointerException("NARG");
            
            // If we are not a KJX, then do not bother trying!
            String ourPath = JarPackageShelf.libraryPath(__ourJar);
            if (!ourPath.endsWith(".kjx") && !ourPath.endsWith(".KJX"))
                return null;
            
            // We need to parse the KJX to handle this
            try (DataInputStream raw = new DataInputStream(
                new RawJarPackageBracketInputStream(__ourJar)))
            {
                // Check magic number
                byte[] magic = new byte[3];
                raw.readFully(magic);
                if (magic[0] != 'K' || magic[1] != 'J' || magic[2] != 'X')
                    throw new IOException("ZZ4j");
                
                // Position of the JAD
                int jadPos = raw.readUnsignedByte();
                
                // Name of the current KJX (ignored, we do not care for it)
                int kjxNameLen = raw.readUnsignedByte();
                raw.skipBytes(kjxNameLen);
                
                // The size of the JAD
                int jadLen = raw.readUnsignedShort();
                
                // Ignore the name of the JAD, we do not care
                int jadNameLen = raw.readUnsignedByte();
                raw.skipBytes(jadNameLen);
                
                // Determine the relative position to where the JAD should
                // start and how much we have read, then skip it
                int currentAt = 3 + 1 + 1 + kjxNameLen + 2 + 1 + jadNameLen;
                if (currentAt < jadPos)
                    raw.skipBytes(jadPos - currentAt);
                
                // Read in the entire JAD
                byte[] jad = new byte[jadLen];
                raw.readFully(jad);
                
                // Use whatever we read as the manifest data
                return new ByteArrayInputStream(jad);
            }
        }
    },
    
    /** JAR Manifest. */
    JAR
    {
        /**
         * {@inheritDoc}
         * @since 2022/03/04
         */
        @Override
        public InputStream manifestStream(JarPackageBracket __ourJar)
            throws IOException, NullPointerException
        {
            if (__ourJar == null)
                throw new NullPointerException("NARG");
            
            // Load the internal manifest
            return JarPackageShelf.openResource(__ourJar,
                "META-INF/MANIFEST.MF");
        }
    },
    
    /* End. */
    ;
    
    /** The available values. */
    public static final Collection<ManifestSourceType> VALUES =
        UnmodifiableCollection.of(Arrays.asList(ManifestSourceType.values()));
    
    /** The number of available values. */
    public static final int COUNT =
        ManifestSourceType.VALUES.size();
    
    /**
     * Returns the manifest stream.
     * 
     * @param __ourJar Our current JAR.
     * @return The stream used to read the manifest or {@code null} if it
     * could not be obtained.
     * @throws IOException If it could not be read.
     * @throws NullPointerException On null arguments.
     * @since 2022/03/04
     */
    public abstract InputStream manifestStream(JarPackageBracket __ourJar)
        throws IOException, NullPointerException;
    
    /**
     * Returns the encoding to use for the manifest.
     * 
     * @return The encoding to use for the manifest.
     * @since 2022/03/04
     */
    public String encoding()
    {
        return "utf-8";
    }
}