Resources.java
上传用户:haobig99
上传日期:2022-06-15
资源大小:369k
文件大小:41k
源码类别:

J2ME

开发平台:

Java

  1. /*
  2.  * Copyright 2008 Sun Microsystems, Inc.  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.  Sun designates this
  8.  * particular file as subject to the "Classpath" exception as provided
  9.  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22.  * CA 95054 USA or visit www.sun.com if you need additional information or
  23.  * have any questions.
  24.  */
  25. package com.sun.lwuit.util;
  26. import com.sun.lwuit.Display;
  27. import com.sun.lwuit.EncodedImage;
  28. import com.sun.lwuit.Font;
  29. import com.sun.lwuit.Image;
  30. import com.sun.lwuit.StaticAnimation;
  31. import com.sun.lwuit.plaf.Border;
  32. import com.sun.lwuit.plaf.Style;
  33. import java.io.ByteArrayInputStream;
  34. import java.io.DataInputStream;
  35. import java.io.IOException;
  36. import java.io.InputStream;
  37. import java.util.Enumeration;
  38. import java.util.Hashtable;
  39. import java.util.Vector;
  40. /**
  41.  * Loads resources from the binary resource file generated during the build process or via the LWUIT Designer.
  42.  * A resource is loaded entirely into memory since random file access is not supported
  43.  * in Java ME, any other approach would be inefficient. This means that memory must
  44.  * be made available to accommodate the resource file. 
  45.  * 
  46.  * @author Shai Almog
  47.  */
  48. public class Resources {
  49.     /**
  50.      * Magic numbers to prevent data corruption
  51.      */
  52.     static final byte MAGIC_THEME_LEGACY = (byte)0xF7;
  53.     static final byte MAGIC_ANIMATION_LEGACY = (byte)0xF8;
  54.     static final byte MAGIC_INDEXED_IMAGE_LEGACY = (byte)0xF4;
  55.     static final byte MAGIC_FONT_LEGACY = (byte)0xF6;
  56.     static final byte MAGIC_INDEXED_FONT_LEGACY = (byte)0xFB;
  57.     static final byte MAGIC_IMAGE_LEGACY = (byte)0xF3;
  58.     static final byte MAGIC_FONT = (byte)0xFC;
  59.     static final byte MAGIC_IMAGE = (byte)0xFD;
  60.     static final byte MAGIC_L10N = (byte)0xF9;
  61.     static final byte MAGIC_DATA = (byte)0xFA;
  62.     static final byte MAGIC_HEADER = (byte)0xFF;
  63.     private short majorVersion;
  64.     private short minorVersion;
  65.     /**
  66.      * Temporary member for compatibility with older versions, in future versions
  67.      * this will superceed the MAGIC_THEME property
  68.      */
  69.     static final byte MAGIC_THEME = (byte)0xF2;
  70.     static final int BORDER_TYPE_EMPTY = 0;
  71.     static final int BORDER_TYPE_LINE = 1;
  72.     static final int BORDER_TYPE_ROUNDED = 2;
  73.     static final int BORDER_TYPE_ETCHED_LOWERED = 4;
  74.     static final int BORDER_TYPE_ETCHED_RAISED = 5;
  75.     static final int BORDER_TYPE_BEVEL_RAISED = 6;
  76.     static final int BORDER_TYPE_BEVEL_LOWERED = 7;
  77.     static final int BORDER_TYPE_IMAGE = 8;
  78.     // for use by the resource editor
  79.     private static Class classLoader = Resources.class;
  80.     private String[] metaData;
  81.     static void setClassLoader(Class cls) {
  82.         classLoader = cls;
  83.     }
  84.     
  85.     /**
  86.      * Hashtable containing the mapping between element types and their names in the
  87.      * resource hashtable
  88.      */
  89.     private Hashtable resourceTypes = new Hashtable();
  90.     
  91.     /**
  92.      * A cache within the resource allowing us to preserve some resources in memory
  93.      * so they can be utilized by a theme when it is loaded
  94.      */
  95.     private Hashtable resources = new Hashtable();
  96.     
  97.     private DataInputStream input; 
  98.     
  99.     // for internal use by the resource editor, creates an empty resource
  100.     Resources() {
  101.     }
  102.     
  103.     Resources(InputStream input) throws IOException {
  104.         openFile(input);
  105.     }
  106.     
  107.     void clear() {
  108.         majorVersion = 0;
  109.         minorVersion = 0;
  110.         resourceTypes.clear();
  111.         resources.clear();
  112.         input = null;
  113.     }
  114.     
  115.     /**
  116.      * This method is used by the LWUIT Designer
  117.      */
  118.     void startingEntry(String id, byte magic) {
  119.     }
  120.     void openFile(InputStream input) throws IOException {
  121.         clear();
  122.         this.input = new DataInputStream(input);
  123.         int resourceCount = this.input.readShort();
  124.         if(resourceCount < 0) {
  125.             throw new IOException("Invalid resource file!");
  126.         }
  127.         for(int iter = 0 ; iter < resourceCount ; iter++) {
  128.             byte magic = this.input.readByte();
  129.             String id = this.input.readUTF();
  130.             startingEntry(id, magic);
  131.             switch(magic) {
  132.                 case MAGIC_HEADER:
  133.                     readHeader();
  134.                     continue;
  135.                 case MAGIC_THEME:
  136.                     setResource(id, MAGIC_THEME, loadTheme(id, magic == MAGIC_THEME));
  137.                     continue;
  138.                 case MAGIC_IMAGE:
  139.                     setResource(id, magic, createImage());
  140.                     continue;
  141.                 case MAGIC_FONT:
  142.                     setResource(id, magic, loadFont(this.input, id, false));
  143.                     continue;
  144.                 case MAGIC_DATA:
  145.                     setResource(id, magic, createData());
  146.                     continue;
  147.                 case MAGIC_L10N:
  148.                     setResource(id, magic, loadL10N());
  149.                     continue;
  150.                 // legacy file support to be removed
  151.                 case MAGIC_IMAGE_LEGACY:
  152.                     setResource(id, MAGIC_IMAGE, createImage());
  153.                     continue;
  154.                 case MAGIC_INDEXED_IMAGE_LEGACY:
  155.                     setResource(id, MAGIC_IMAGE, createPackedImage8());
  156.                     continue;
  157.                 case MAGIC_THEME_LEGACY:
  158.                     setResource(id, MAGIC_THEME, loadTheme(id, magic == MAGIC_THEME));
  159.                     continue;
  160.                 case MAGIC_FONT_LEGACY:
  161.                     setResource(id, MAGIC_FONT, loadFont(this.input, id, false));
  162.                     continue;
  163.                 case MAGIC_INDEXED_FONT_LEGACY:
  164.                     setResource(id, MAGIC_FONT, loadFont(this.input, id, true));
  165.                     continue;
  166.                 case MAGIC_ANIMATION_LEGACY:
  167.                     setResource(id, MAGIC_IMAGE, loadAnimation(this.input));
  168.                     continue;
  169.                 default:
  170.                     throw new IOException("Corrupt theme file unrecognized magic number: " + Integer.toHexString(magic & 0xff));
  171.             }
  172.         }
  173.     }
  174.     
  175.     /**
  176.      * Reads the header of the resource file
  177.      */
  178.     private void readHeader() throws IOException {
  179.         int size = input.readShort();
  180.         majorVersion = input.readShort();
  181.         minorVersion = input.readShort();
  182.         
  183.         metaData = new String[input.readShort()];
  184.         for(int iter = 0 ; iter < metaData.length ; iter++) {
  185.             metaData[iter] = input.readUTF();
  186.         }
  187.     }
  188.     /**
  189.      * Returns the version number for this resource file. 
  190.      * This value relates to the value from the header defined by the resource file
  191.      * specification. 0 is returned for legacy versions of the resource file format.
  192.      * 
  193.      * @return major version number for the resource file
  194.      */
  195.     public int getMajorVersion() {
  196.         return majorVersion;
  197.     }
  198.     /**
  199.      * Returns the minor version number for this resource file
  200.      * This value relates to the value from the header defined by the resource file
  201.      * specification. 
  202.      *
  203.      * @return minor version number for the resource file
  204.      */
  205.     public int getMinorVersion() {
  206.         return minorVersion;
  207.     }
  208.     /**
  209.      * Returns optional meta-data associated with the resource file
  210.      *
  211.      * @return optional meta-data associated with the file
  212.      */
  213.     public String[] getMetaData() {
  214.         return metaData;
  215.     }
  216.     /**
  217.      * Returns the names of the resources within this bundle
  218.      * 
  219.      * @return array of names of all the resources in this bundle
  220.      */
  221.     public String[] getResourceNames() {
  222.         String[] arr = new String[resourceTypes.size()];
  223.         Enumeration e = resourceTypes.keys();
  224.         for(int iter = 0 ; iter < arr.length ; iter++) {
  225.             arr[iter] = (String)e.nextElement();
  226.         }
  227.         return arr;
  228.     }
  229.     /**
  230.      * Returns the names of the data resources within this bundle
  231.      * 
  232.      * @return array of names of the data resources in this bundle
  233.      */
  234.     public String[] getDataResourceNames() {
  235.         return getResourceTypeNames(MAGIC_DATA);
  236.     }
  237.     /**
  238.      * For internal use only
  239.      */
  240.     void setResource(String id, byte type, Object value) {
  241.         if(value == null) {
  242.             resources.remove(id);
  243.             resourceTypes.remove(id);
  244.         } else {
  245.             resources.put(id, value);
  246.             resourceTypes.put(id, new Byte(type));
  247.         }
  248.     }
  249.     
  250.     /**
  251.      * Returns the names of the localization bundles within this bundle
  252.      * 
  253.      * @return array of names of the localization resources in this bundle
  254.      */
  255.     public String[] getL10NResourceNames() {
  256.         return getResourceTypeNames(MAGIC_L10N);
  257.     }
  258.     /**
  259.      * Returns the names of the fonts within this bundle
  260.      * 
  261.      * @return array of names of the font resources in this bundle
  262.      */
  263.     public String[] getFontResourceNames() {
  264.         Vector vec = new Vector();
  265.         Enumeration e = resourceTypes.keys();
  266.         while(e.hasMoreElements()) {
  267.             String c = (String)e.nextElement();
  268.             if(isFont(c)) {
  269.                 vec.addElement(c);
  270.             }
  271.         }
  272.         return toStringArray(vec);
  273.     }
  274.     /**
  275.      * Returns the names of the images within this bundle
  276.      * 
  277.      * @return array of names of the image resources in this bundle
  278.      */
  279.     public String[] getThemeResourceNames() {
  280.         Vector vec = new Vector();
  281.         Enumeration e = resourceTypes.keys();
  282.         while(e.hasMoreElements()) {
  283.             String c = (String)e.nextElement();
  284.             if(isTheme(c)) {
  285.                 vec.addElement(c);
  286.             }
  287.         }
  288.         return toStringArray(vec);
  289.     }
  290.     /**
  291.      * Returns the names of the images within this bundle
  292.      * 
  293.      * @return array of names of the image resources in this bundle
  294.      */
  295.     public String[] getImageResourceNames() {
  296.         Vector vec = new Vector();
  297.         Enumeration e = resourceTypes.keys();
  298.         while(e.hasMoreElements()) {
  299.             String c = (String)e.nextElement();
  300.             if(isImage(c)) {
  301.                 vec.addElement(c);
  302.             }
  303.         }
  304.         return toStringArray(vec);
  305.     }
  306.     /**
  307.      * Returns the names of the animations within this bundle
  308.      * 
  309.      * @return array of names of the animation resources in this bundle
  310.      * @deprecated use getImageResourceNames instead
  311.      */
  312.     public String[] getAnimationResourceNames() {
  313.         return getResourceTypeNames(MAGIC_ANIMATION_LEGACY);
  314.     }
  315.     
  316.     /**
  317.      * For internal use only
  318.      */
  319.     byte getResourceType(String name) {
  320.         return ((Byte)resourceTypes.get(name)).byteValue();
  321.     }
  322.     
  323.     private String[] getResourceTypeNames(byte b) {
  324.         Vector vec = new Vector();
  325.         Enumeration e = resourceTypes.keys();
  326.         while(e.hasMoreElements()) {
  327.             String c = (String)e.nextElement();
  328.             if(((Byte)resourceTypes.get(c)).byteValue() == b) {
  329.                 vec.addElement(c);
  330.             }
  331.         }
  332.         return toStringArray(vec);
  333.     }
  334.     private static String[] toStringArray(Vector v) {
  335.         String[] s = new String[v.size()];
  336.         for(int iter = 0 ; iter < s.length ; iter++) {
  337.             s[iter] = (String)v.elementAt(iter);
  338.         }
  339.         return s;
  340.     }
  341.     /**
  342.      * Returns true if this is a generic data resource
  343.      * 
  344.      * @param name the name of the resource
  345.      * @return true if the resource is a data resource
  346.      * @throws NullPointerException if the resource doesn't exist
  347.      */
  348.     public boolean isL10N(String name) {
  349.         byte b = ((Byte)resourceTypes.get(name)).byteValue();
  350.         return b == MAGIC_L10N;
  351.     }
  352.     
  353.     /**
  354.      * Returns true if this is a theme resource
  355.      * 
  356.      * @param name the name of the resource
  357.      * @return true if the resource is a theme
  358.      * @throws NullPointerException if the resource doesn't exist
  359.      */
  360.     public boolean isTheme(String name) {
  361.         byte b = ((Byte)resourceTypes.get(name)).byteValue();
  362.         return b == MAGIC_THEME_LEGACY || b == MAGIC_THEME;
  363.     }
  364.     /**
  365.      * Returns true if this is a font resource
  366.      * 
  367.      * @param name the name of the resource
  368.      * @return true if the resource is a font
  369.      * @throws NullPointerException if the resource doesn't exist
  370.      */
  371.     public boolean isFont(String name) {
  372.         byte b = ((Byte)resourceTypes.get(name)).byteValue();
  373.         return b == MAGIC_FONT || b == MAGIC_FONT_LEGACY || b == MAGIC_INDEXED_FONT_LEGACY;
  374.     }
  375.     /**
  376.      * Returns true if this is an animation resource
  377.      * 
  378.      * @param name the name of the resource
  379.      * @return true if the resource is an animation
  380.      * @throws NullPointerException if the resource doesn't exist
  381.      * @deprecated animations are no longer distinguished from images in the resource file, use Image.isAnimation instead
  382.      */
  383.     public boolean isAnimation(String name) {
  384.         byte b = ((Byte)resourceTypes.get(name)).byteValue();
  385.         return b == MAGIC_ANIMATION_LEGACY;
  386.     }
  387.     /**
  388.      * Returns true if this is a data resource
  389.      * 
  390.      * @param name the name of the resource
  391.      * @return true if the resource is a data resource
  392.      * @throws NullPointerException if the resource doesn't exist
  393.      */
  394.     public boolean isData(String name) {
  395.         byte b = ((Byte)resourceTypes.get(name)).byteValue();
  396.         return b == MAGIC_DATA;
  397.     }
  398.     /**
  399.      * Returns true if this is an image resource
  400.      * 
  401.      * @param name the name of the resource
  402.      * @return true if the resource is an image
  403.      * @throws NullPointerException if the resource doesn't exist
  404.      */
  405.     public boolean isImage(String name) {
  406.         byte b = ((Byte)resourceTypes.get(name)).byteValue();
  407.         return b == MAGIC_IMAGE_LEGACY || b == MAGIC_ANIMATION_LEGACY || b == MAGIC_INDEXED_IMAGE_LEGACY || b == MAGIC_IMAGE;
  408.     }
  409.     
  410.     /**
  411.      * Creates a resource object from the local JAR resource identifier
  412.      * 
  413.      * @param resource a local reference to a resource using the syntax of Class.getResourceAsStream(String)
  414.      * @return a resource object
  415.      * @throws java.io.IOException if opening/reading the resource fails
  416.      */
  417.     public static Resources open(String resource) throws IOException {
  418.         try {
  419.             InputStream is = Display.getInstance().getResourceAsStream(classLoader, resource);
  420.             Resources r = new Resources(is);
  421.             is.close();
  422.             return r;
  423.         } catch(RuntimeException err) {
  424.             // intercept exceptions since user code might not deal well with runtime exceptions 
  425.             err.printStackTrace();
  426.             throw new IOException(err.getMessage());
  427.         }
  428.     }
  429.     StaticAnimation loadAnimation(DataInputStream input) throws IOException {
  430.         return StaticAnimation.createAnimation(input);
  431.     }
  432.     
  433.     /**
  434.      * Creates a resource object from the given input stream
  435.      * 
  436.      * @param resource stream from which to read the resource
  437.      * @return a resource object
  438.      * @throws java.io.IOException if opening/reading the resource fails
  439.      */
  440.     public static Resources open(InputStream resource) throws IOException {
  441.         return new Resources(resource);
  442.     }
  443.     /**
  444.      * Returns the image resource from the file
  445.      * 
  446.      * @param id name of the image resource
  447.      * @return cached image instance
  448.      */
  449.     public Image getImage(String id) {
  450.         return (Image)resources.get(id);
  451.     }
  452.     /**
  453.      * Returns the animation resource from the file
  454.      * 
  455.      * @param id name of the animation resource
  456.      * @return cached image instance
  457.      * @deprecated use getImage(String) instead
  458.      */
  459.     public StaticAnimation getAnimation(String id) {
  460.         return (StaticAnimation)resources.get(id);
  461.     }
  462.     
  463.     /**
  464.      * Returns the data resource from the file
  465.      * 
  466.      * @param id name of the data resource
  467.      * @return newly created input stream that allows reading the data of the resource
  468.      */
  469.     public InputStream getData(String id) {
  470.         return new ByteArrayInputStream((byte[])resources.get(id));
  471.     }
  472.     
  473.     /**
  474.      * Returns a hashmap containing localized String key/value pairs for the given locale name
  475.      * 
  476.      * @param id the name of the locale resource
  477.      * @param locale name of the locale resource
  478.      * @return Hashtable containing key value pairs for localized data
  479.      */
  480.     public Hashtable getL10N(String id, String locale) {
  481.         return (Hashtable)((Hashtable)resources.get(id)).get(locale);
  482.     }
  483.     /**
  484.      * Returns an enumration of the locales supported by this resource id
  485.      * 
  486.      * @param id the name of the locale resource
  487.      * @return enumeration of strings containing bundle names
  488.      */
  489.     public Enumeration listL10NLocales(String id) {
  490.         return ((Hashtable)resources.get(id)).keys();
  491.     }
  492.     /**
  493.      * Returns the font resource from the file
  494.      * 
  495.      * @param id name of the font resource
  496.      * @return cached font instance
  497.      */
  498.     public Font getFont(String id) {
  499.         return (Font)resources.get(id);
  500.     }
  501.     
  502.     /**
  503.      * Returns the theme resource from the file
  504.      * 
  505.      * @param id name of the theme resource
  506.      * @return cached theme instance
  507.      */
  508.     public Hashtable getTheme(String id) {
  509.         Hashtable h = (Hashtable)resources.get(id);
  510.         
  511.         // theme can be null in valid use cases such as the resource editor
  512.         if(h != null && h.containsKey("uninitialized")) {
  513.             Enumeration e = h.keys();
  514.             while(e.hasMoreElements()) {
  515.                 String key = (String)e.nextElement();
  516.                 if(key.endsWith("font") || (key.endsWith("Image") && !key.endsWith("scaledImage"))) {
  517.                     Object value = h.get(key);
  518.                     if(value == null) {
  519.                         throw new IllegalArgumentException("Couldn't find resource: " + key);
  520.                     }
  521.                     
  522.                     // the resource was not already loaded when we loaded the theme
  523.                     // it must be loaded now so we can resolve the temporary name
  524.                     if(value instanceof String) {
  525.                         Object o = resources.get(value);
  526.                         if(o == null) {
  527.                             throw new IllegalArgumentException("Theme entry for " + key + " could not be found: " + value);
  528.                         }
  529.                         h.put(key, o);
  530.                     }
  531.                 }
  532.                 // if this is a border we might need to do additional work for older versions
  533.                 // of LWUIT and for the case of an image border where the images might not have
  534.                 // been loaded yet when the border was created
  535.                 if(key.endsWith("order")) {
  536.                     Border b = confirmBorder(h, key);
  537.                     if(majorVersion == 0 && minorVersion == 0) {
  538.                         b.setPressedInstance(confirmBorder(h, key + "Pressed"));
  539.                         b.setFocusedInstance(confirmBorder(h, key + "Focused"));
  540.                         h.remove(key + "Pressed");
  541.                         h.remove(key + "Focused");
  542.                     }
  543.                     h.put(key, b);
  544.                 }
  545.             }
  546.             h.remove("uninitialized");
  547.         }
  548.         return h;
  549.     }
  550.     
  551.     private Border confirmBorder(Hashtable h, String key) {
  552.         Object val = h.get(key);
  553.         if(val == null) {
  554.             return null;
  555.         }
  556.         if(!(val instanceof Border)) {
  557.             String[] value = (String[])val;
  558.             if(value == null) {
  559.                 throw new IllegalArgumentException("Couldn't find resource: " + key);
  560.             }
  561.             // the resource was not already loaded when we loaded the theme
  562.             // it must be loaded now so we can resolve the temporary name
  563.             Border imageBorder = createImageBorder(value);
  564.             return imageBorder;
  565.         }
  566.         return (Border)val;
  567.     }
  568.     
  569.     private Border createImageBorder(String[] value) {
  570.         Image[] images = new Image[value.length];
  571.         for(int iter = 0 ; iter < value.length ; iter++) {
  572.             images[iter] = (Image)resources.get(value[iter]);
  573.         }
  574.         switch(images.length) {
  575.             case 2:
  576.                return Border.createImageBorder(images[0], images[1], null); 
  577.             case 3:
  578.                return Border.createImageBorder(images[0], images[1], images[2]); 
  579.             case 8:
  580.                return Border.createImageBorder(images[0], images[1], images[2],
  581.                    images[3], images[4], images[5], images[6], images[7], null); 
  582.             default:
  583.                return Border.createImageBorder(images[0], images[1], images[2],
  584.                    images[3], images[4], images[5], images[6], images[7], images[8]); 
  585.         }
  586.     }
  587.     
  588.     Object getResourceObject(String res) {
  589.         return resources.get(res);
  590.     }
  591.     
  592.     Image createImage() throws IOException {
  593.         if(majorVersion == 0 && minorVersion == 0) {
  594.             byte[] data = new byte[input.readInt()];
  595.             input.readFully(data, 0, data.length);
  596.             return EncodedImage.create(data);
  597.         } else {
  598.             int type = input.readByte() & 0xff;
  599.             switch(type) {
  600.                 // PNG file
  601.                 case 0xf1:
  602.                 // JPEG File
  603.                 case 0xf2:
  604.                     byte[] data = new byte[input.readInt()];
  605.                     input.readFully(data, 0, data.length);
  606.                     return EncodedImage.create(data);
  607.                 // Indexed image
  608.                 case 0xf3:
  609.                     return createPackedImage8();
  610.                 // animation
  611.                 case 0xf4:
  612.                     return loadAnimation(input);
  613.                 // SVG
  614.                 case 0xf5:
  615.                     int svgSize = input.readInt();
  616.                     if(Image.isSVGSupported()) {
  617.                         byte[] s = new byte[svgSize];
  618.                         input.readFully(s);
  619.                         String baseURL = input.readUTF();
  620.                         boolean animated = input.readBoolean();
  621.                         loadSVGRatios(input);
  622.                         byte[] fallback = new byte[input.readInt()];
  623.                         if(fallback.length > 0) {
  624.                             input.readFully(fallback, 0, fallback.length);
  625.                         }
  626.                         return Image.createSVG(baseURL, animated, s);
  627.                     } else {
  628.                         svgSize -= input.skip(svgSize);
  629.                         while(svgSize > 0) {
  630.                             svgSize -= input.skip(svgSize);
  631.                         }
  632.                         // read the base url, the animated property and screen ratios to skip them as well...
  633.                         input.readUTF();
  634.                         input.readBoolean();
  635.                         input.readFloat();
  636.                         input.readFloat();
  637.                         byte[] fallback = new byte[input.readInt()];
  638.                         input.readFully(fallback, 0, fallback.length);
  639.                         return EncodedImage.create(fallback);                      
  640.                     }
  641.                 // Fail this is the wrong data type
  642.                 default:
  643.                     throw new IOException("Illegal type while creating image: " + Integer.toHexString(type));
  644.             }
  645.         }
  646.     }
  647.     void loadSVGRatios(DataInputStream input) throws IOException {
  648.         input.readFloat();
  649.         input.readFloat();
  650.     }
  651.     private byte[] createData() throws IOException {
  652.         byte[] data = new byte[input.readInt()];
  653.         input.readFully(data);
  654.         return data;
  655.     }
  656.     Font loadFont(DataInputStream input, String id, boolean packed) throws IOException {
  657.         if(majorVersion == 0 && minorVersion == 0) {
  658.             Image bitmap;
  659.             if(packed) {
  660.                 bitmap = createPackedImage8();
  661.             } else {
  662.                 bitmap = createImage();
  663.             }
  664.             int charCount = input.readShort();
  665.             int[] cutOffsets = new int[charCount];
  666.             int[] charWidth = new int[charCount];
  667.             for(int iter = 0 ; iter < charCount ; iter++) {
  668.                 cutOffsets[iter] = input.readShort();
  669.                 charWidth[iter] = input.readByte();
  670.             }
  671.             String charset = input.readUTF();
  672.             Font old = Font.getBitmapFont(id);
  673.             if(old != null) {
  674.                 return old;
  675.             }
  676.             return Font.createBitmapFont(id, bitmap, cutOffsets, charWidth, charset);
  677.         }
  678.         // read a system font fallback
  679.         int fallback = input.readByte() & 0xff;
  680.         // do we have an emedded truetype font? Do we support embedded fonts?
  681.         boolean trueTypeIncluded = input.readBoolean();
  682.         Font font = null;
  683.         if(trueTypeIncluded) {
  684.             int size = input.readInt();
  685.             if(Font.isTrueTypeFileSupported()) {
  686.                 font = Font.createTrueTypeFont(input);
  687.             } else {
  688.                 while(size > 0) {
  689.                     size -= input.skip(size);
  690.                 }
  691.             }
  692.         }
  693.         boolean lookupIncluded = input.readBoolean();
  694.         if(lookupIncluded) {
  695.             String lookup = input.readUTF();
  696.             if(font == null && Font.isCreationByStringSupported()) {
  697.                 font = Font.create(lookup);
  698.             }
  699.         }
  700.         boolean bitmapIncluded = input.readBoolean();
  701.         if(bitmapIncluded) {
  702.             font = loadBitmapFont(input, id, font);
  703.         }
  704.         if(font != null) {
  705.             return font;
  706.         }
  707.         return Font.createSystemFont(fallback & (Font.FACE_MONOSPACE | Font.FACE_PROPORTIONAL | Font.FACE_SYSTEM),
  708.                 fallback & (Font.STYLE_BOLD | Font.STYLE_ITALIC | Font.STYLE_PLAIN | Font.STYLE_UNDERLINED),
  709.                 fallback & (Font.SIZE_LARGE | Font.SIZE_MEDIUM| Font.SIZE_SMALL));
  710.     }
  711.     void readRenderingHint(DataInputStream i) throws IOException {
  712.         i.readByte();
  713.     }
  714.     Font loadBitmapFont(DataInputStream input, String id, com.sun.lwuit.Font font) throws IOException {
  715.         Image bitmap = createImage();
  716.         int charCount = input.readShort();
  717.         int[] cutOffsets = new int[charCount];
  718.         int[] charWidth = new int[charCount];
  719.         for(int iter = 0 ; iter < charCount ; iter++) {
  720.             cutOffsets[iter] = input.readShort();
  721.         }
  722.         for(int iter = 0 ; iter < charCount ; iter++) {
  723.             charWidth[iter] = input.readByte();
  724.         }
  725.         String charset = input.readUTF();
  726.         readRenderingHint(input);
  727.         if(font == null) {
  728.             if(Font.isBitmapFontEnabled()) {
  729.                 Font old = Font.getBitmapFont(id);
  730.                 if(old != null) {
  731.                     // Returning bitmap font from cache, to prevent collision with an
  732.                     // old resource file use Font.clearBitmapCache()
  733.                     return old;
  734.                 }
  735.                 return Font.createBitmapFont(id, bitmap, cutOffsets, charWidth, charset);
  736.             }
  737.         }
  738.         return font;
  739.     }
  740.     Hashtable loadTheme(String id, boolean newerVersion) throws IOException {
  741.         Hashtable theme = new Hashtable();
  742.         
  743.         theme.put("name", id);
  744.         
  745.         // marks the theme as uninitialized so we can finish "wiring" cached resources
  746.         theme.put("uninitialized", Boolean.TRUE);
  747.         int size = input.readShort();
  748.         for(int iter = 0 ; iter < size ; iter++) {
  749.             String key = input.readUTF();
  750.             // if this is a simple numeric value
  751.             if(key.endsWith("Color")) {
  752.                 theme.put(key, Integer.toHexString(input.readInt()));
  753.                 continue;
  754.             } 
  755.             // if this is a short numeric value for transparency
  756.             if(key.endsWith("ransparency")) {
  757.                 theme.put(key, "" + (input.readByte() & 0xff));
  758.                 continue;
  759.             } 
  760.             // if this is a padding or margin then we will have the 4 values as bytes
  761.             if(key.endsWith("adding") || key.endsWith("argin")) {
  762.                 int p1 = input.readByte() & 0xff;
  763.                 int p2 = input.readByte() & 0xff;
  764.                 int p3 = input.readByte() & 0xff;
  765.                 int p4 = input.readByte() & 0xff;
  766.                 theme.put(key, "" + p1 + "," + p2 + "," + p3 + "," + p4);
  767.                 continue;
  768.             }
  769.             // border
  770.             if(key.endsWith("order")) {
  771.                 if(majorVersion == 0 && minorVersion == 0) {
  772.                     theme.put(key, createBorder(input, newerVersion));
  773.                     if(newerVersion) {
  774.                         if(input.readBoolean()) {
  775.                             theme.put(key + "Pressed", createBorder(input, true));
  776.                         }
  777.                         if(input.readBoolean()) {
  778.                             theme.put(key + "Focused", createBorder(input, true));
  779.                         }
  780.                     }
  781.                 } else {
  782.                     int borderType = input.readShort() & 0xffff;
  783.                     Object b = createBorder(input, borderType);
  784.                     theme.put(key, b);
  785.                 }
  786.                 continue;
  787.             }
  788.             // if this is a font
  789.             if(key.endsWith("ont")) {
  790.                 Font f;
  791.                 // is this a new font?
  792.                 if(input.readBoolean()) {
  793.                     String fontId = input.readUTF();
  794.                     f = (Font)resources.get(fontId);
  795.                     
  796.                     // if the font is not yet loaded
  797.                     if(f == null) {
  798.                         theme.put(key, fontId);
  799.                         continue;
  800.                     }
  801.                 } else {
  802.                     f = Font.createSystemFont(input.readByte(), input.readByte(), input.readByte());
  803.                 }
  804.                 theme.put(key, f);
  805.                 continue;
  806.             } 
  807.             // the background property
  808.             if(key.endsWith("ackground")) {
  809.                 int type = input.readByte() & 0xff;
  810.                 int pos = key.indexOf('.');
  811.                 if(pos > -1) {
  812.                     key = key.substring(0, pos);
  813.                 } else {
  814.                     key = "";
  815.                 }
  816.                 theme.put(key + Style.BACKGROUND_TYPE, new Byte((byte)type));
  817.                 switch(type) {
  818.                     // Scaled Image
  819.                     case 0xF1:
  820.                     // Tiled Both Image
  821.                     case 0xF4:
  822.                         // the image name coupled with the type
  823.                         theme.put(key + Style.BG_IMAGE, input.readUTF());
  824.                         break;
  825.                     // Aligned Image
  826.                     case 0xF5:
  827.                     // Tiled Vertically Image
  828.                     case 0xF2:
  829.                     // Tiled Horizontally Image
  830.                     case 0xF3:
  831.                         // the image name coupled with the type and with alignment information
  832.                         String imageName = input.readUTF();
  833.                         theme.put(key + Style.BG_IMAGE, imageName);
  834.                         byte align = input.readByte();
  835.                         theme.put(key + Style.BACKGROUND_ALIGNMENT, new Byte(align));
  836.                         break;
  837.                     // Horizontal Linear Gradient
  838.                     case 0xF6:
  839.                     // Vertical Linear Gradient
  840.                     case 0xF7:
  841.                         Float c =  new Float(0.5f);
  842.                         theme.put(key + Style.BACKGROUND_GRADIENT, new Object[] {new Integer(input.readInt()), new Integer(input.readInt()),c, c, new Float(1)});
  843.                         break;
  844.                         
  845.                     // Radial Gradient
  846.                     case 0xF8:
  847.                         int c1  = input.readInt();
  848.                         int c2 = input.readInt();
  849.                         float f1 = input.readFloat();
  850.                         float f2 = input.readFloat();
  851.                         float radialSize = 1;
  852.                         if(minorVersion > 1) {
  853.                             radialSize = input.readFloat();
  854.                         }
  855.                         theme.put(key + Style.BACKGROUND_GRADIENT, new Object[] {new Integer(c1),
  856.                             new Integer(c2),
  857.                             new Float(f1),
  858.                             new Float(f2),
  859.                             new Float(radialSize)});
  860.                         break;
  861.                 }
  862.                 continue;
  863.             }
  864.             // if this is a background image bgImage
  865.             if(key.endsWith("bgImage")) {
  866.                 String imageId = input.readUTF();
  867.                 Image i = (Image)resources.get(imageId);
  868.                 // if the font is not yet loaded
  869.                 if(i == null) {
  870.                     theme.put(key, imageId);
  871.                     continue;
  872.                 }
  873.                 theme.put(key, i);
  874.                 continue;
  875.             } 
  876.             if(key.endsWith("scaledImage")) {
  877.                 if(input.readBoolean()) {
  878.                     theme.put(key, "true");
  879.                 } else {
  880.                     theme.put(key, "false");
  881.                 }
  882.                 continue;
  883.             }
  884.             
  885.             if(key.endsWith(Style.BACKGROUND_TYPE) || key.endsWith(Style.BACKGROUND_ALIGNMENT)) {
  886.                 theme.put(key, new Byte(input.readByte()));
  887.                 continue;
  888.             }
  889.             if(key.endsWith(Style.BACKGROUND_GRADIENT)) {
  890.                 if(minorVersion < 2) {
  891.                     theme.put(key, new Object[] {
  892.                         new Integer(input.readInt()),
  893.                         new Integer(input.readInt()),
  894.                         new Float(input.readFloat()),
  895.                         new Float(input.readFloat())
  896.                     });
  897.                 } else {
  898.                     theme.put(key, new Object[] {
  899.                         new Integer(input.readInt()),
  900.                         new Integer(input.readInt()),
  901.                         new Float(input.readFloat()),
  902.                         new Float(input.readFloat()),
  903.                         new Float(input.readFloat())
  904.                     });
  905.                 }
  906.                 continue;
  907.             }
  908.             // thow an exception no idea what this is
  909.             throw new IOException("Error while trying to read theme property: " + key);
  910.         }
  911.         return theme;
  912.     }
  913.     
  914.     private Object createBorder(DataInputStream input, int type) throws IOException {
  915.         switch(type) {
  916.             // empty border
  917.             case 0xff01:
  918.                 return Border.getEmpty();
  919.             // Line border
  920.             case 0xff02:
  921.                 // use theme colors?
  922.                 if(input.readBoolean()) {
  923.                     return Border.createLineBorder(input.readByte());
  924.                 } else {
  925.                     return Border.createLineBorder(input.readByte(), input.readInt());
  926.                 }
  927.             // Rounded border
  928.             case 0xff03:
  929.                 // use theme colors?
  930.                 if(input.readBoolean()) {
  931.                     return Border.createRoundBorder(input.readByte(), input.readByte());
  932.                 } else {
  933.                     return Border.createRoundBorder(input.readByte(), input.readByte(), input.readInt());
  934.                 }
  935.             // Etched Lowered border
  936.             case 0xff04:
  937.                 // use theme colors?
  938.                 if(input.readBoolean()) {
  939.                     return Border.createEtchedLowered();
  940.                 } else {
  941.                     return Border.createEtchedLowered(input.readInt(), input.readInt());
  942.                 }
  943.             // Etched raised border
  944.             case 0xff05:
  945.                 // use theme colors?
  946.                 if(input.readBoolean()) {
  947.                     return Border.createEtchedRaised();
  948.                 } else {
  949.                     return Border.createEtchedRaised(input.readInt(), input.readInt());
  950.                 }
  951.             // Bevel raised
  952.             case 0xff07:
  953.                 // use theme colors?
  954.                 if(input.readBoolean()) {
  955.                     return Border.createBevelRaised();
  956.                 } else {
  957.                     return Border.createBevelRaised(input.readInt(), input.readInt(), input.readInt(), input.readInt());
  958.                 }
  959.             // Bevel lowered
  960.             case 0xff06:
  961.                 // use theme colors?
  962.                 if(input.readBoolean()) {
  963.                     return Border.createBevelLowered();
  964.                 } else {
  965.                     return Border.createBevelLowered(input.readInt(), input.readInt(), input.readInt(), input.readInt());
  966.                 }
  967.             // Image border
  968.             case 0xff08:
  969.                 Object[] imageBorder = readImageBorder(input);
  970.                 return imageBorder;
  971.         }
  972.         return null;
  973.     }
  974.     private Object createBorder(DataInputStream input, boolean newerVersion) throws IOException {
  975.         int type = input.readByte();
  976.         switch(type) {
  977.             case BORDER_TYPE_EMPTY:
  978.                 return Border.getEmpty();
  979.             case BORDER_TYPE_LINE:
  980.                 // use theme colors?
  981.                 if(input.readBoolean()) {
  982.                     return Border.createLineBorder(input.readByte());
  983.                 } else {
  984.                     return Border.createLineBorder(input.readByte(), input.readInt());
  985.                 }
  986.             case BORDER_TYPE_ROUNDED:
  987.                 // use theme colors?
  988.                 if(input.readBoolean()) {
  989.                     return Border.createRoundBorder(input.readByte(), input.readByte());
  990.                 } else {
  991.                     return Border.createRoundBorder(input.readByte(), input.readByte(), input.readInt());
  992.                 }
  993.             case BORDER_TYPE_ETCHED_LOWERED:
  994.                 // use theme colors?
  995.                 if(input.readBoolean()) {
  996.                     return Border.createEtchedLowered();
  997.                 } else {
  998.                     return Border.createEtchedLowered(input.readInt(), input.readInt());
  999.                 }
  1000.             case BORDER_TYPE_ETCHED_RAISED:
  1001.                 // use theme colors?
  1002.                 if(input.readBoolean()) {
  1003.                     return Border.createEtchedRaised();
  1004.                 } else {
  1005.                     return Border.createEtchedRaised(input.readInt(), input.readInt());
  1006.                 }
  1007.             case BORDER_TYPE_BEVEL_RAISED:
  1008.                 // use theme colors?
  1009.                 if(input.readBoolean()) {
  1010.                     return Border.createBevelRaised();
  1011.                 } else {
  1012.                     return Border.createBevelRaised(input.readInt(), input.readInt(), input.readInt(), input.readInt());
  1013.                 }
  1014.             case BORDER_TYPE_BEVEL_LOWERED:
  1015.                 // use theme colors?
  1016.                 if(input.readBoolean()) {
  1017.                     return Border.createBevelLowered();
  1018.                 } else {
  1019.                     return Border.createBevelLowered(input.readInt(), input.readInt(), input.readInt(), input.readInt());
  1020.                 }
  1021.             case BORDER_TYPE_IMAGE:
  1022.                 Object[] imageBorder = readImageBorder(input);
  1023.                 
  1024.                 if(!newerVersion) {
  1025.                     // legacy issue...
  1026.                     input.readBoolean();
  1027.                 }
  1028.                 
  1029.                 return imageBorder;
  1030.         }
  1031.         return null;
  1032.     }
  1033.     
  1034.     private String[] readImageBorder(DataInputStream input) throws IOException {
  1035.         // Read number of images can be 2, 3, 8 or 9
  1036.         int size = input.readByte();
  1037.         String[] imageBorder = new String[size];
  1038.                 
  1039.         for(int iter = 0 ; iter < size ; iter++) {
  1040.             imageBorder[iter] = input.readUTF();
  1041.         }
  1042.         return imageBorder;
  1043.     }
  1044.         
  1045.     private Hashtable loadL10N() throws IOException {
  1046.         Hashtable l10n = new Hashtable();
  1047.         int keys = input.readShort();
  1048.         int languages = input.readShort();
  1049.         String[] keyArray = new String[keys];
  1050.         for(int iter = 0 ; iter < keys ; iter++) {
  1051.             String key = input.readUTF();
  1052.             keyArray[iter] = key;
  1053.         }
  1054.         for(int iter = 0 ; iter < languages ; iter++) {        
  1055.             Hashtable currentLanguage = new Hashtable();
  1056.             String lang = input.readUTF();
  1057.             l10n.put(lang, currentLanguage);
  1058.             for(int valueIter =  0 ; valueIter < keys ; valueIter++) {
  1059.                 currentLanguage.put(keyArray[valueIter], input.readUTF());
  1060.             }
  1061.         }
  1062.         return l10n;
  1063.     }
  1064.             
  1065.     /**
  1066.      * Creates a packed image from the input stream for an 8 bit packed image
  1067.      */
  1068.     private Image createPackedImage8() throws IOException {
  1069.         // read the length of the palette;
  1070.         int size = input.readByte() & 0xff;
  1071.         
  1072.         // 0 means the last bit overflowed, there is no sense for 0 sized palette
  1073.         if(size == 0) {
  1074.             size = 256;
  1075.         }
  1076.         int[] palette = new int[size];
  1077.         for(int iter = 0 ; iter < palette.length ; iter++) {
  1078.             palette[iter] = input.readInt();
  1079.         }
  1080.         int width = input.readShort();
  1081.         int height = input.readShort();
  1082.         byte[] data = new byte[width * height];
  1083.         input.readFully(data, 0, data.length);
  1084.         return Image.createIndexed(width, height, palette, data);
  1085.     }
  1086. }