0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: *
0026: */
0027:
0028: /**
0029: * This tool takes XML file describing skin as input and produces Java
0030: * and C files with romized skin's properties values. Those files are
0031: * intended to be compiled with the rest of Chameleon source files.
0032: *
0033: */package com.sun.midp.skinromization;
0034:
0035: import java.io.*;
0036: import java.util.*;
0037:
0038: import javax.xml.parsers.*;
0039: import org.w3c.dom.*;
0040:
0041: import java.awt.image.*;
0042: import java.awt.*;
0043: import com.sun.midp.imageutil.*;
0044: import java.lang.reflect.*;
0045: import com.sun.midp.chameleon.skins.resources.*;
0046:
0047: /**
0048: * Represents romization job
0049: */
0050: class RomizationJob {
0051: /** XML file name to get skin properties from */
0052: public String skinXMLFileName = "";
0053:
0054: /** Skin images directory name */
0055: public String skinImagesDirName = "";
0056:
0057: /** Output file name for RomizedSkin class */
0058: public String outBinFileName = "";
0059:
0060: /** Output file name for romized images data */
0061: public String outCFileName = "";
0062:
0063: /** For QA purposes: overrides images romization settings from XML */
0064: public String imageRomOverride = "";
0065: }
0066:
0067: /**
0068: * Main tool class
0069: */
0070: public class SkinRomizationTool {
0071: /** Romizer that does actual romization */
0072: private static SkinRomizer romizer = null;
0073:
0074: /** Romization job to perform */
0075: private static RomizationJob romizationJob = null;
0076:
0077: /** Print debug output while running */
0078: private static boolean debug = false;
0079:
0080: /** Print usage info and exit */
0081: private static boolean printHelp = false;
0082:
0083: /**
0084: * Main method
0085: *
0086: * @param args Command line arguments
0087: */
0088: public static void main(String[] args) {
0089: try {
0090: parseArgs(args);
0091: if (printHelp) {
0092: printHelp();
0093: return;
0094: }
0095:
0096: if (!validateParsedArgs()) {
0097: System.exit(1);
0098: }
0099:
0100: romizer = new SkinRomizer(debug);
0101: romizer.romize(romizationJob);
0102: } catch (Exception e) {
0103: e.printStackTrace();
0104: System.exit(1);
0105: }
0106: }
0107:
0108: /**
0109: * Parses command line arguments
0110: *
0111: * @param args command line arguments
0112: */
0113: private static void parseArgs(String[] args) {
0114: romizationJob = new RomizationJob();
0115:
0116: for (int i = 0; i < args.length; ++i) {
0117: String arg = args[i];
0118: if (arg.equals("-xml")) {
0119: romizationJob.skinXMLFileName = args[++i];
0120: } else if (arg.equals("-outbin")) {
0121: romizationJob.outBinFileName = args[++i];
0122: } else if (arg.equals("-imagedir")) {
0123: romizationJob.skinImagesDirName = args[++i];
0124: } else if (arg.equals("-outc")) {
0125: romizationJob.outCFileName = args[++i];
0126: // this option is for QA purposes only and therefore
0127: // hidden, undocumented and unsupported
0128: } else if (arg.equals("-qaimagerom")) {
0129: romizationJob.imageRomOverride = args[++i];
0130: } else if (arg.equals("-debug")) {
0131: debug = true;
0132: } else {
0133: throw new IllegalArgumentException("invalid option \""
0134: + args[i] + "\"");
0135: }
0136: }
0137: }
0138:
0139: /**
0140: * Validates parsed arguments, printing error message
0141: * if some arg is invalid.
0142: *
0143: * @return true, if all arguments are valid
0144: */
0145: private static boolean validateParsedArgs() {
0146: File f = new File(romizationJob.skinXMLFileName);
0147: if (!f.isFile()) {
0148: System.err
0149: .println("SkinRomizationTool: Couldn't find input XML file: "
0150: + '"' + romizationJob.skinXMLFileName + '"');
0151:
0152: return false;
0153: }
0154:
0155: f = new File(romizationJob.skinImagesDirName);
0156: if (!f.isDirectory()) {
0157: System.err.println("SkinRomizationTool: " + '"'
0158: + romizationJob.skinImagesDirName + '"'
0159: + " isn't a directory");
0160:
0161: return false;
0162: }
0163:
0164: return true;
0165: }
0166:
0167: /**
0168: * Prints usage information
0169: */
0170: private static void printHelp() {
0171: /**
0172: * Following options are recognized:
0173: * -xml: XML file describing skin.
0174: * -out: Output file. If empty, output will be to stdout.
0175: * -help: Print usage information
0176: * -debug: Be verbose: print some debug info while running.
0177: *
0178: */
0179: System.err
0180: .println("Usage: java -jar "
0181: + "com.sun.midp.SkinRomizationTool "
0182: + "-xml <localXMLFile> "
0183: + "-imagedir <skinImagesDirName> "
0184: + "-outbin <localOutputBinFile> "
0185: + "-outc <localOutputCFile> " + "[-debug] "
0186: + "[-help]");
0187: }
0188: }
0189:
0190: /**
0191: * Base class for all skin properties
0192: */
0193: abstract class SkinPropertyBase {
0194:
0195: /** Print some debug info */
0196: static boolean debug = false;
0197:
0198: /** Name of property ID */
0199: String idName;
0200:
0201: /** Integer number used as property ID */
0202: int id;
0203:
0204: /** Offset of the property value(s) in romized values array */
0205: int valueOffset;
0206:
0207: /**
0208: * Constructor
0209: *
0210: * @param idName name of the property's ID
0211: * @param id property's ID
0212: * @exception IllegalArgumentException if some of parameters
0213: * are invalid
0214: */
0215: SkinPropertyBase(String idName, String id) {
0216: this .idName = idName;
0217:
0218: try {
0219: this .id = Integer.parseInt(id);
0220: } catch (NumberFormatException e) {
0221: throw new IllegalArgumentException("Skin property " + '"'
0222: + idName + '"' + " has illegal ID value: " + '"'
0223: + id + '"');
0224: }
0225:
0226: // offset will be set later
0227: this .valueOffset = -1;
0228: }
0229:
0230: /**
0231: * Tests if two properties have the same value
0232: *
0233: * @param prop property to compare value with
0234: * @return true if properties have the same value
0235: */
0236: abstract boolean isEqualValue(SkinPropertyBase prop);
0237:
0238: /**
0239: * How many entries in values array is required for
0240: * storing this property value
0241: *
0242: * @return number of array entries required for storing
0243: * the value of this property
0244: */
0245: abstract int getValueOffsetDelta();
0246:
0247: /**
0248: * Prints values array entries for this property's value
0249: *
0250: * @param writer where to print entries
0251: * @param indent indentation string for each entry
0252: */
0253: abstract void outputValue(BinaryOutputStream out)
0254: throws java.io.IOException;
0255:
0256: /**
0257: * Factory method: constructs SkinProperty from DOM node that
0258: * corresponds to the property description in XML file.`
0259: *
0260: * @param n DOM node that corresponds to the property
0261: * description in XML file
0262: * @return SkinPropertyBase object constructed from DOM node
0263: * @exception IllegalArgumentException if DOM node is invalid
0264: */
0265: static SkinPropertyBase valueOf(Node n)
0266: throws IllegalArgumentException {
0267:
0268: if (debug) {
0269: System.err.println("Property node: " + n.getNodeName());
0270: }
0271:
0272: SkinPropertyBase p = null;
0273:
0274: String idName = getAttributeValue(n, "Key");
0275: String id = getAttributeValue(n, "KeyValue");
0276: String value = getAttributeValue(n, "Value");
0277:
0278: if (debug) {
0279: System.err.println("\tID name: " + idName);
0280: System.err.println("\tID: " + id);
0281: System.err.println("\tValue: " + value);
0282: System.err.println("");
0283: }
0284:
0285: String nodeName = n.getNodeName();
0286:
0287: if (nodeName.equals("integer")) {
0288: p = new IntSkinProperty(idName, id, value);
0289: } else if (nodeName.equals("integer_seq")) {
0290: p = new IntSeqSkinProperty(idName, id, value);
0291: } else if (nodeName.equals("string")) {
0292: p = new StringSkinProperty(idName, id, value);
0293: } else if (nodeName.equals("font")) {
0294: p = new FontSkinProperty(idName, id, value);
0295: } else if (nodeName.equals("image")) {
0296: String isRomized = getAttributeValue(n, "Romized");
0297: p = new ImageSkinProperty(idName, id, value, isRomized);
0298: } else if (nodeName.equals("composite_image")) {
0299: String totalPieces = getAttributeValue(n, "Pieces");
0300: String isRomized = getAttributeValue(n, "Romized");
0301: p = new CompositeImageSkinProperty(idName, id, value,
0302: totalPieces, isRomized);
0303: } else {
0304: throw new IllegalArgumentException(
0305: "Illegal skin property node: " + '"' + nodeName
0306: + '"');
0307: }
0308:
0309: return p;
0310: }
0311:
0312: /**
0313: * Helper method: gets attribute value from node with some
0314: * errors checking.
0315: *
0316: * @param n DOM node to get attribute value from
0317: * @param attrName attribute name
0318: * @return requested attribute value
0319: */
0320: private static String getAttributeValue(Node n, String attrName) {
0321: Element e = (Element) n;
0322: String attrValue = e.getAttribute(attrName);
0323:
0324: return attrValue.trim();
0325: }
0326:
0327: /**
0328: * Helper method for renaming symbolic property value coming
0329: * from XML file (constant name) into another constant name.
0330: * Needed for backward compatibility.
0331: *
0332: * @param value original value
0333: * @return renamed value
0334: */
0335: protected static String renameSymbolicValue(String value) {
0336: /**
0337: * Value can be in form of "Graphics.VALUE" where "VALUE" is
0338: * the name of one of the constants defined in Graphics class.
0339: * Convert such value into "SkinResourcesConstants.VALUE".
0340: * The reason why "SkinResourcesConstants.VALUE" is not used
0341: * directly in XML file is backward compatibility.
0342: */
0343: if (value.startsWith("Graphics.")) {
0344: int dotIdx = value.indexOf(".");
0345: value = "SkinResourcesConstants" + value.substring(dotIdx);
0346: }
0347:
0348: /**
0349: * Value can be in form of "ScrollIndSkin.VALUE" where "VALUE" is
0350: * the name of one of the constants defined in
0351: * ScrollIndResourcesConstants class.
0352: * Convert such value into "ScrollIndResourcesConstants.VALUE".
0353: * The reason why "ScrollIndResourcesConstants.VALUE" is not
0354: * used directly in XML file is backward compatibility.
0355: */
0356: if (value.startsWith("ScrollIndSkin.")) {
0357: int dotIdx = value.indexOf(".");
0358: value = "ScrollIndResourcesConstants"
0359: + value.substring(dotIdx);
0360: }
0361:
0362: /**
0363: * Value can be in form of "FontResources.VALUE" where "VALUE" is
0364: * the name of one of the constants defined in
0365: * FontResourcesConstants class.
0366: * Convert such value into "FontResourcesConstants.VALUE".
0367: * The reason why "FontResourcesConstants.VALUE" is not
0368: * used directly in XML file is backward compatibility.
0369: */
0370: if (value.startsWith("FontResources.")) {
0371: int dotIdx = value.indexOf(".");
0372: value = "FontResourcesConstants" + value.substring(dotIdx);
0373: }
0374:
0375: return value;
0376: }
0377:
0378: /**
0379: * Helper method for convertic symbolic property value into int value.
0380: * Basically, it converts integer constant name into constant value.
0381: *
0382: * @param value symbolic value
0383: * @return integer value
0384: */
0385: protected static int symbolicValueToInt(String value) {
0386: int dotIdx = value.indexOf(".");
0387: if (dotIdx == -1 || dotIdx == value.length() - 1) {
0388: throw new IllegalArgumentException();
0389: }
0390:
0391: String className = value.substring(0, dotIdx);
0392: if (className.length() == 0) {
0393: throw new IllegalArgumentException();
0394: }
0395:
0396: className = "com.sun.midp.chameleon.skins.resources."
0397: + className;
0398: Class c = null;
0399: try {
0400: c = Class.forName(className);
0401: } catch (Throwable t) {
0402: throw new IllegalArgumentException();
0403: }
0404:
0405: String fieldName = value.substring(dotIdx + 1);
0406: if (fieldName.length() == 0) {
0407: throw new IllegalArgumentException();
0408: }
0409:
0410: Field f = null;
0411: try {
0412: f = c.getField(fieldName);
0413: } catch (Throwable t) {
0414: throw new IllegalArgumentException();
0415: }
0416:
0417: int val = 0;
0418: try {
0419: val = f.getInt(null);
0420: } catch (Throwable t) {
0421: throw new IllegalArgumentException();
0422: }
0423:
0424: return val;
0425: }
0426:
0427: /**
0428: * Property value can be primitive arithmetic expression.
0429: * This helper method evaluates such expression. Operators
0430: * precedence is simple, left to right.
0431: *
0432: * @param exp expression to evaluate
0433: * @return integer expression value
0434: */
0435: protected static int evalValueExpression(String exp) {
0436: int result = 0;
0437: char op = '=';
0438:
0439: StringTokenizer st = new StringTokenizer(exp);
0440: while (true) {
0441: String v = renameSymbolicValue(st.nextToken());
0442:
0443: int intVal = 0;
0444: try {
0445: int radix = 10;
0446: if (v.startsWith("0x")) {
0447: v = v.substring(2);
0448: radix = 16;
0449: }
0450: intVal = Integer.parseInt(v, radix);
0451: } catch (NumberFormatException e) {
0452: intVal = symbolicValueToInt(v);
0453: }
0454:
0455: switch (op) {
0456: case '=':
0457: result = intVal;
0458: break;
0459:
0460: case '|':
0461: result |= intVal;
0462: break;
0463:
0464: default:
0465: throw new IllegalArgumentException();
0466: }
0467:
0468: if (!st.hasMoreTokens()) {
0469: break;
0470: }
0471:
0472: String opStr = st.nextToken();
0473: if (opStr.length() > 1) {
0474: throw new IllegalArgumentException();
0475: }
0476: op = (opStr.toCharArray())[0];
0477: }
0478:
0479: return result;
0480: }
0481: }
0482:
0483: /**
0484: * Integer property
0485: */
0486: class IntSkinProperty extends SkinPropertyBase {
0487:
0488: /**
0489: * Property's value: integer number
0490: */
0491: int value;
0492:
0493: /**
0494: * Constructor
0495: *
0496: * @param idName name of the property's ID
0497: * @param id property's ID
0498: * @param value integer value as string
0499: */
0500: IntSkinProperty(String idName, String id, String value) {
0501: super (idName, id);
0502:
0503: String v = value;
0504: try {
0505: this .value = evalValueExpression(v);
0506: } catch (Throwable t) {
0507: throw new IllegalArgumentException(
0508: "Invalid int property value: " + value);
0509: }
0510: }
0511:
0512: /**
0513: * Tests if two properties have the same value
0514: *
0515: * @param prop property to compare value with
0516: * @return true if properties have the same value
0517: */
0518: boolean isEqualValue(SkinPropertyBase prop) {
0519: if (!(prop instanceof IntSkinProperty)) {
0520: return false;
0521: }
0522: IntSkinProperty p = (IntSkinProperty) prop;
0523:
0524: return (p.value == value);
0525: }
0526:
0527: /**
0528: * How many entries in values array is required for
0529: * storing this property value
0530: *
0531: * @return number of array entries required for storing
0532: * the value of this property
0533: */
0534: int getValueOffsetDelta() {
0535: // For performance optimization, integers values aren't stored in
0536: // separate values array as in case for other properties. So there
0537: // is no offset delta for integer value.
0538: return 0;
0539: }
0540:
0541: /**
0542: * Prints values array entries for this property's value
0543: *
0544: * @param writer where to print entries
0545: * @param indent indentation string for each entry
0546: */
0547: void outputValue(BinaryOutputStream out) throws java.io.IOException {
0548:
0549: out.writeInt(value);
0550: }
0551: }
0552:
0553: /**
0554: * Integers sequence property
0555: */
0556: class IntSeqSkinProperty extends SkinPropertyBase {
0557:
0558: /**
0559: * Propety's value: array of integers
0560: */
0561: int[] value;
0562:
0563: /**
0564: * Constructor
0565: *
0566: * @param idName name of the property's ID
0567: * @param id property's ID
0568: * @param value comma separated integer values as string
0569: */
0570: IntSeqSkinProperty(String idName, String id, String value) {
0571: super (idName, id);
0572:
0573: // find number of integers in sequence
0574: int seqLen = 0;
0575: char[] chars = value.toCharArray();
0576: int numStart = 0;
0577: for (int i = 0; i <= chars.length; ++i) {
0578: if (i == chars.length || chars[i] == ',') {
0579: seqLen++;
0580: numStart = i + 1;
0581: }
0582: }
0583:
0584: this .value = new int[seqLen];
0585:
0586: // fill values array
0587: int idx = 0;
0588: numStart = 0;
0589: for (int i = 0; i <= chars.length; ++i) {
0590: if (i == chars.length || chars[i] == ',') {
0591: String v = (value.substring(numStart, i)).trim();
0592: int intVal = 0;
0593: try {
0594: intVal = evalValueExpression(v);
0595: } catch (Throwable t) {
0596: throw new IllegalArgumentException(
0597: "Invalid integer sequence property value: "
0598: + value);
0599: }
0600:
0601: this .value[idx++] = intVal;
0602: numStart = i + 1;
0603: }
0604: }
0605: }
0606:
0607: /**
0608: * Tests if two properties have the same value
0609: *
0610: * @param prop property to compare value with
0611: * @return true if properties have the same value
0612: */
0613: boolean isEqualValue(SkinPropertyBase prop) {
0614: if (!(prop instanceof IntSeqSkinProperty)) {
0615: return false;
0616: }
0617: IntSeqSkinProperty p = (IntSeqSkinProperty) prop;
0618:
0619: if (p.value.length != value.length) {
0620: return false;
0621: }
0622:
0623: for (int i = 0; i < value.length; ++i) {
0624: if (p.value[i] != value[i]) {
0625: return false;
0626: }
0627: }
0628:
0629: return true;
0630: }
0631:
0632: /**
0633: * How many entries in values array is required for
0634: * storing this property value
0635: *
0636: * @return number of array entries required for storing
0637: * the value of this property
0638: */
0639: int getValueOffsetDelta() {
0640: // integers sequence value is stored as length of
0641: // the sequence followed by sequence integers
0642: return (1 + value.length);
0643: }
0644:
0645: /**
0646: * Prints values array entries for this property's value
0647: *
0648: * @param writer where to print entries
0649: * @param indent indentation string for each entry
0650: */
0651: void outputValue(BinaryOutputStream out) throws java.io.IOException {
0652:
0653: // out sequence length
0654: out.writeInt(value.length);
0655:
0656: // out sequence integers
0657: for (int i = 0; i < value.length; ++i) {
0658: out.writeInt(value[i]);
0659: }
0660: }
0661: }
0662:
0663: /**
0664: * String property
0665: */
0666: class StringSkinProperty extends SkinPropertyBase {
0667:
0668: /**
0669: * Property's value: string
0670: */
0671: String value;
0672:
0673: /**
0674: * Constructor
0675: *
0676: * @param idName name of the property's ID
0677: * @param id property's ID
0678: * @param value string value
0679: */
0680: StringSkinProperty(String idName, String id, String value) {
0681: super (idName, id);
0682: this .value = value;
0683: }
0684:
0685: /**
0686: * Tests if two properties have the same value
0687: *
0688: * @param prop property to compare value with
0689: * @return true if properties have the same value
0690: */
0691: boolean isEqualValue(SkinPropertyBase prop) {
0692: if (!(prop instanceof StringSkinProperty)) {
0693: return false;
0694: }
0695: StringSkinProperty p = (StringSkinProperty) prop;
0696:
0697: return p.value.equals(value);
0698: }
0699:
0700: /**
0701: * How many entries in values array is required for
0702: * storing this property value
0703: *
0704: * @return number of array entries required for storing
0705: * the value of this property
0706: */
0707: int getValueOffsetDelta() {
0708: return 1;
0709: }
0710:
0711: /**
0712: * Prints values array entries for this property's value
0713: *
0714: * @param writer where to print entries
0715: * @param indent indentation string for each entry
0716: */
0717: void outputValue(BinaryOutputStream out) throws java.io.IOException {
0718:
0719: out.writeString(value);
0720: }
0721: }
0722:
0723: /**
0724: * Font property
0725: */
0726: class FontSkinProperty extends SkinPropertyBase {
0727:
0728: /**
0729: * Property's value: integer font type
0730: */
0731: int value;
0732:
0733: /**
0734: * Constructor
0735: *
0736: * @param idName name of the property's ID
0737: * @param id property's ID
0738: * @param value integer font ID as string
0739: */
0740: FontSkinProperty(String idName, String id, String value) {
0741: super (idName, id);
0742:
0743: String v = value;
0744: try {
0745: this .value = evalValueExpression(v);
0746: } catch (Throwable t) {
0747: throw new IllegalArgumentException(
0748: "Invalid font property value: " + value);
0749: }
0750: }
0751:
0752: /**
0753: * Tests if two properties have the same value
0754: *
0755: * @param prop property to compare value with
0756: * @return true if properties have the same value
0757: */
0758: boolean isEqualValue(SkinPropertyBase prop) {
0759: if (!(prop instanceof FontSkinProperty)) {
0760: return false;
0761: }
0762: FontSkinProperty p = (FontSkinProperty) prop;
0763:
0764: return (p.value == value);
0765: }
0766:
0767: /**
0768: * How many entries in values array is required for
0769: * storing this property value
0770: *
0771: * @return number of array entries required for storing
0772: * the value of this property
0773: */
0774: int getValueOffsetDelta() {
0775: return 1;
0776: }
0777:
0778: /**
0779: * Prints values array entries for this property's value
0780: *
0781: * @param writer where to print entries
0782: * @param indent indentation string for each entry
0783: */
0784: void outputValue(BinaryOutputStream out) throws java.io.IOException {
0785:
0786: out.writeInt(value);
0787: }
0788: }
0789:
0790: /**
0791: * Image property
0792: */
0793: class ImageSkinProperty extends SkinPropertyBase {
0794:
0795: /**
0796: * Property's value: image file name without extension
0797: */
0798: String value;
0799:
0800: /**
0801: * Romize this image
0802: */
0803: boolean isRomized;
0804:
0805: /**
0806: * True, if image value has been specified,
0807: * i.e. it is not an empty string
0808: */
0809: boolean hasValue;
0810:
0811: /** total unique images for all properties */
0812: static int totalImages;
0813:
0814: /**
0815: * Constructor
0816: *
0817: * @param idName name of the property's ID
0818: * @param id property's ID
0819: * @param value image file name without extension
0820: * @param isRomized boolean value as string
0821: * @exception IllegalArgumentException if some of parameters
0822: * are invalid
0823: */
0824: ImageSkinProperty(String idName, String id, String value,
0825: String isRomized) throws IllegalArgumentException {
0826:
0827: super (idName, id);
0828: this .value = value;
0829:
0830: if (isRomized.equals("true")) {
0831: this .isRomized = true;
0832: } else if (isRomized.equals("false")) {
0833: this .isRomized = false;
0834: } else {
0835: throw new IllegalArgumentException("Skin property " + '"'
0836: + idName + '"' + " has illegal romization value: "
0837: + '"' + isRomized + '"');
0838: }
0839:
0840: hasValue = true;
0841: if (value.equals("")) {
0842: hasValue = false;
0843: }
0844: }
0845:
0846: /**
0847: * Tests if two properties have the same value
0848: *
0849: * @param prop property to compare value with
0850: * @return true if properties have the same value
0851: */
0852: boolean isEqualValue(SkinPropertyBase prop) {
0853: if (!(prop instanceof ImageSkinProperty)) {
0854: return false;
0855: }
0856: ImageSkinProperty p = (ImageSkinProperty) prop;
0857:
0858: return p.value.equals(value);
0859: }
0860:
0861: /**
0862: * How many entries in values array is required for
0863: * storing this property value
0864: *
0865: * @return number of array entries required for storing
0866: * the value of this property
0867: */
0868: int getValueOffsetDelta() {
0869: return 1;
0870: }
0871:
0872: /**
0873: * Prints values array entries for this property's value
0874: *
0875: * @param writer where to print entries
0876: * @param indent indentation string for each entry
0877: */
0878: void outputValue(BinaryOutputStream out) throws java.io.IOException {
0879:
0880: out.writeString(value);
0881: }
0882: }
0883:
0884: /**
0885: * Composite image property. Composite image is an image that consists
0886: * of several pieces, where each piece is some image file.
0887: */
0888: class CompositeImageSkinProperty extends SkinPropertyBase {
0889: /**
0890: * Property's value: pieces files names without extension.
0891: * File name for each piece is constructed from base prefix by
0892: * addition of piece number, i.e. if the prefix is "image" and
0893: * number of pieces is 2, then file name for first piece is
0894: * "image0" and for the second piece is "image1".
0895: */
0896: String[] value;
0897:
0898: /**
0899: * Number of pieces
0900: */
0901: int totalPieces;
0902:
0903: /**
0904: * Romize this image
0905: */
0906: boolean isRomized;
0907:
0908: /**
0909: * True, if image value has been specified,
0910: * i.e. it is not an empty string
0911: */
0912: boolean hasValue;
0913:
0914: /** total unique images for all properties */
0915: static int totalImages;
0916:
0917: /**
0918: * Constructor
0919: *
0920: * @param idName name of the property's ID
0921: * @param id property's ID
0922: * @param value base prefix for pieces files names
0923: * @param totalPieces number of pieces in composite image
0924: * @param isRomized boolean value as string
0925: * @exception IllegalArgumentException if some of parameters
0926: * are invalid
0927: */
0928: CompositeImageSkinProperty(String idName, String id, String value,
0929: String totalPieces, String isRomized)
0930: throws IllegalArgumentException {
0931:
0932: super (idName, id);
0933:
0934: try {
0935: this .totalPieces = Integer.parseInt(totalPieces);
0936: } catch (NumberFormatException e) {
0937: throw new IllegalArgumentException(
0938: "Composite image skin property " + '"' + idName
0939: + '"' + " has illegal total pieces value: "
0940: + '"' + totalPieces + '"');
0941: }
0942:
0943: this .value = new String[this .totalPieces];
0944: for (int i = 0; i < this .totalPieces; ++i) {
0945: String v = "";
0946: if (!value.equals("")) {
0947: v = value + i;
0948: }
0949:
0950: this .value[i] = v;
0951: }
0952:
0953: if (isRomized.equals("true")) {
0954: this .isRomized = true;
0955: } else if (isRomized.equals("false")) {
0956: this .isRomized = false;
0957: } else {
0958: throw new IllegalArgumentException("Skin property " + '"'
0959: + idName + '"' + " has illegal romization value: "
0960: + '"' + isRomized + '"');
0961: }
0962:
0963: hasValue = true;
0964: if (value.equals("")) {
0965: hasValue = false;
0966: }
0967: }
0968:
0969: /**
0970: * Tests if two properties have the same value
0971: *
0972: * @param prop property to compare value with
0973: * @return true if properties have the same value
0974: */
0975: boolean isEqualValue(SkinPropertyBase prop) {
0976: if (!(prop instanceof CompositeImageSkinProperty)) {
0977: return false;
0978: }
0979: CompositeImageSkinProperty p = (CompositeImageSkinProperty) prop;
0980:
0981: if (p.value.length != value.length) {
0982: return false;
0983: }
0984:
0985: boolean equal = true;
0986: for (int i = 0; i < value.length; ++i) {
0987: if (!(p.value[i].equals(value[i]))) {
0988: equal = false;
0989: break;
0990: }
0991: }
0992:
0993: return equal;
0994: }
0995:
0996: /**
0997: * How many entries in values array is required for
0998: * storing this property value
0999: *
1000: * @return number of array entries required for storing
1001: * the value of this property
1002: */
1003: int getValueOffsetDelta() {
1004: // for composite image, values array contain
1005: // file names of image pieces
1006: return value.length;
1007: }
1008:
1009: /**
1010: * Prints values array entries for this property's value
1011: *
1012: * @param writer where to print entries
1013: * @param indent indentation string for each entry
1014: */
1015: void outputValue(BinaryOutputStream out) throws java.io.IOException {
1016:
1017: // output pieces file names
1018: for (int i = 0; i < value.length; ++i) {
1019: out.writeString(value[i]);
1020: }
1021: }
1022: }
1023:
1024: /**
1025: * Represents romized image
1026: */
1027: final class RomizedImage {
1028: /** romized image data */
1029: byte[] imageData;
1030:
1031: /** romized image index */
1032: int imageIndex;
1033:
1034: /**
1035: * Constructor
1036: *
1037: * @param imageData romized image data
1038: * @param imageIndex romized image index
1039: */
1040: RomizedImage(byte imageData[], int imageIndex) {
1041: this .imageData = imageData;
1042: this .imageIndex = imageIndex;
1043: }
1044:
1045: /**
1046: * Prints romized image data as C array
1047: *
1048: * @param writer where to print
1049: * @param indent indent string for each row
1050: * @param maxColumns max number of columns
1051: */
1052: void printDataArray(PrintWriter writer, String indent,
1053: int maxColumns) {
1054: int len = imageData.length;
1055:
1056: writer.print(indent);
1057: for (int i = 0; i < len; i++) {
1058: writer.print(toHex(imageData[i]));
1059: if (i != len - 1) {
1060: writer.print(", ");
1061:
1062: if ((i > 0) && ((i + 1) % maxColumns == 0)) {
1063: writer.println("");
1064: writer.print(indent);
1065: }
1066: }
1067: }
1068: }
1069:
1070: /**
1071: * Converts byte to a hex string
1072: *
1073: * @param b byte value to convert
1074: * @return hex representation of byte
1075: */
1076: private static String toHex(byte b) {
1077: Integer I = new Integer((((int) b) << 24) >>> 24);
1078: int i = I.intValue();
1079:
1080: if (i < (byte) 16) {
1081: return "0x0" + Integer.toString(i, 16);
1082: } else {
1083: return "0x" + Integer.toString(i, 16);
1084: }
1085: }
1086: }
1087:
1088: /**
1089: * Creates RomizedImage instances
1090: */
1091: final class RomizedImageFactory {
1092: /** for converting image into raw format */
1093: ImageToRawConverter converter;
1094:
1095: /** romized images counter */
1096: int romizedImageCounter = 0;
1097:
1098: /**
1099: * Constructor
1100: *
1101: * @param converter converter to use for converting images
1102: * into raw format
1103: */
1104: private RomizedImageFactory(ImageToRawConverter converter) {
1105: this .converter = converter;
1106: }
1107:
1108: /**
1109: * Creates RomizedImage from BufferedImage object
1110: *
1111: * @param image image to create romized image from
1112: * @param imageIndex romized image index
1113: * @return created RomizedImage
1114: */
1115: RomizedImage createFromBufferedImage(BufferedImage image,
1116: int imageIndex) {
1117:
1118: int width = image.getWidth(null);
1119: int height = image.getHeight(null);
1120: boolean hasAlpha = image.getColorModel().hasAlpha();
1121: int[] imageData = getBufferedImageData(image);
1122:
1123: byte[] rawData = converter.convertToRaw(imageData, width,
1124: height, hasAlpha);
1125:
1126: return new RomizedImage(rawData, romizedImageCounter++);
1127: }
1128:
1129: /**
1130: * Utility method. Gets BuffredImage data as in array.
1131: *
1132: * @param image BufferedImage to return data for
1133: * @return image data as byte array
1134: */
1135: private static int[] getBufferedImageData(BufferedImage image) {
1136: int width = image.getWidth(null);
1137: int height = image.getHeight(null);
1138: BufferedImage bi = new BufferedImage(width, height,
1139: BufferedImage.TYPE_INT_ARGB);
1140:
1141: Graphics g = bi.getGraphics();
1142: try {
1143: g.drawImage(image, 0, 0, width, height, null);
1144: } finally {
1145: g.dispose();
1146: }
1147:
1148: DataBuffer srcBuf = bi.getData().getDataBuffer();
1149: int[] buf = ((DataBufferInt) srcBuf).getData();
1150:
1151: return buf;
1152: }
1153:
1154: /**
1155: * Returns factory class instance
1156: *
1157: * @param rawFormat format of raw file
1158: * @param colorFormat format of pixel in raw file
1159: * @param endian romized image data endianess
1160: * @return RomizedImage factory
1161: */
1162: static RomizedImageFactory getFactory(int rawFormat,
1163: int colorFormat, int endian) {
1164:
1165: ImageToRawConverter converter = new ImageToRawConverter(
1166: rawFormat, colorFormat, endian);
1167:
1168: return new RomizedImageFactory(converter);
1169: }
1170: }
1171:
1172: /**
1173: * Binary output stream capable of writing data
1174: * in big/little endian format.
1175: */
1176: final class BinaryOutputStream {
1177: /** Underlying stream for writing bytes into */
1178: private DataOutputStream outputStream = null;
1179:
1180: /** true for big endian format, false for little */
1181: private boolean isBigEndian = false;
1182:
1183: /**
1184: * Constructor
1185: *
1186: * @param out underlying output stream for writing bytes into
1187: * @param isBigEndian true for big endian format, false for little
1188: */
1189: BinaryOutputStream(OutputStream out, boolean isBigEndian) {
1190: this .outputStream = new DataOutputStream(out);
1191: this .isBigEndian = isBigEndian;
1192: }
1193:
1194: /**
1195: * Writes byte value into stream
1196: *
1197: * @param value byte value to write
1198: */
1199: public void writeByte(int value) throws java.io.IOException {
1200:
1201: outputStream.writeByte(value);
1202: }
1203:
1204: /**
1205: * Writes integer value into stream
1206: *
1207: * @param value integer value to write
1208: */
1209: public void writeInt(int value) throws java.io.IOException {
1210:
1211: if (isBigEndian) {
1212: outputStream.writeByte((value >> 24) & 0xFF);
1213: outputStream.writeByte((value >> 16) & 0xFF);
1214: outputStream.writeByte((value >> 8) & 0xFF);
1215: outputStream.writeByte(value & 0xFF);
1216: } else {
1217: outputStream.writeByte(value & 0xFF);
1218: outputStream.writeByte((value >> 8) & 0xFF);
1219: outputStream.writeByte((value >> 16) & 0xFF);
1220: outputStream.writeByte((value >> 24) & 0xFF);
1221: }
1222: }
1223:
1224: /**
1225: * Writes string into stream. The string data is written
1226: * in follwoing order:
1227: * - Number of bytes for string chars
1228: * - Encoding (US ASCII or UTF8)
1229: * - String chars as bytes
1230: *
1231: * The number of bytes for string chars is written as
1232: * single byte, so it can't exceed 255.
1233: *
1234: * @param value String value to write into stream
1235: */
1236: public void writeString(String value) throws java.io.IOException {
1237:
1238: byte[] chars = value.getBytes("UTF8");
1239: int length = chars.length;
1240:
1241: // determine what encoding to use
1242: int encoding = SkinResourcesConstants.STRING_ENCODING_USASCII;
1243: for (int i = 0; i < length; ++i) {
1244: int ch = chars[i] & 0xFF;
1245: if (ch >= 128) {
1246: encoding = SkinResourcesConstants.STRING_ENCODING_UTF8;
1247: break;
1248: }
1249: }
1250:
1251: if (encoding == SkinResourcesConstants.STRING_ENCODING_UTF8) {
1252: System.err.println("UTF8: " + value);
1253: // for '\0' at the end of the string
1254: length += 1;
1255: }
1256:
1257: // write string data length
1258: if (length > 255) {
1259: throw new IllegalArgumentException(
1260: "String data length exceeds 255 bytes");
1261: }
1262: outputStream.writeByte(length);
1263:
1264: // write string encoding
1265: outputStream.writeByte(encoding);
1266:
1267: // write string data
1268: for (int i = 0; i < chars.length; ++i) {
1269: outputStream.writeByte(chars[i] & 0xFF);
1270: }
1271:
1272: if (encoding == SkinResourcesConstants.STRING_ENCODING_UTF8) {
1273: // '\0' at the end of the string
1274: outputStream.writeByte(0);
1275: }
1276: }
1277:
1278: /**
1279: * Closes stream
1280: */
1281: public void close() throws java.io.IOException {
1282:
1283: outputStream.close();
1284: }
1285: }
1286:
1287: /**
1288: * Perform the romization
1289: */
1290: class SkinRomizer {
1291: /** current romization job */
1292: RomizationJob romizationJob;
1293:
1294: /** XML with skin properties as Document */
1295: private Document domDoc = null;
1296:
1297: /** Be verbose: print some debug info while running */
1298: private boolean debug = false;
1299:
1300: /** All skin properties */
1301: Vector allProps = null;
1302:
1303: /** All integers sequence skin properties */
1304: Vector intSeqProps = null;
1305:
1306: /** All string skin properties */
1307: Vector stringProps = null;
1308:
1309: /** All integers font skin properties */
1310: Vector fontProps = null;
1311:
1312: /** All image skin properties */
1313: Vector imageProps = null;
1314:
1315: /** All composite image skin properties */
1316: Vector compImageProps = null;
1317:
1318: /** All romized images */
1319: Vector romizedImages = null;
1320:
1321: /** Romized image factory */
1322: RomizedImageFactory romizedImageFactory;
1323:
1324: /** Character output file writer */
1325: PrintWriter writer = null;
1326:
1327: /** Binary output file stream */
1328: BinaryOutputStream outputStream = null;
1329:
1330: /** raw image file format */
1331: int rawFormat = ImageToRawConverter.FORMAT_INVALID;
1332:
1333: /** raw image color format */
1334: int colorFormat = ImageToRawConverter.FORMAT_INVALID;
1335:
1336: /** raw image data endianess */
1337: int endianFormat = ImageToRawConverter.FORMAT_INVALID;
1338:
1339: /** array of strings used in XML file to specify raw image format */
1340: static String[] imageFormatStrings = {
1341: // raw file formats
1342: "Putpixel", "ARGB",
1343: // endianess
1344: "Little", "Big",
1345: // supported pixel formats
1346: "565", "888", };
1347:
1348: /** array of constants corresponding to strings above */
1349: static int[] imageFormats = {
1350: // raw file formats
1351: ImageToRawConverter.RAW_FORMAT_PP,
1352: ImageToRawConverter.RAW_FORMAT_ARGB,
1353: // endianess
1354: ImageToRawConverter.INT_FORMAT_LITTLE_ENDIAN,
1355: ImageToRawConverter.INT_FORMAT_BIG_ENDIAN,
1356: // supported pixel formats
1357: ImageToRawConverter.COLOR_FORMAT_565,
1358: ImageToRawConverter.COLOR_FORMAT_888 };
1359:
1360: /**
1361: * Constructor
1362: *
1363: * @param dbg print some debug output while running
1364: */
1365: public SkinRomizer(boolean dbg) {
1366: debug = dbg;
1367: SkinPropertyBase.debug = dbg;
1368:
1369: }
1370:
1371: /**
1372: * Does the actual romization
1373: *
1374: * @param romizationJob romization to perform
1375: * @exception Exception if there was an error during romization
1376: */
1377: public void romize(RomizationJob romizationJob) throws Exception {
1378:
1379: this .romizationJob = romizationJob;
1380:
1381: allProps = new Vector();
1382: intSeqProps = new Vector();
1383: stringProps = new Vector();
1384: fontProps = new Vector();
1385: imageProps = new Vector();
1386: compImageProps = new Vector();
1387:
1388: // load XML file as DOM tree
1389: DocumentBuilderFactory domFactory = DocumentBuilderFactory
1390: .newInstance();
1391:
1392: // do not validate input XML file,
1393: // we assume it has been validated before
1394: domFactory.setValidating(false);
1395:
1396: DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
1397: domDoc = domBuilder.parse(new File(
1398: romizationJob.skinXMLFileName));
1399:
1400: // traverse DOM tree constructed fro input XML and
1401: // collect all skin properties described there
1402: collectSkinProperties(domDoc.getDocumentElement());
1403:
1404: // now, when we know format of romized images,
1405: // we can create romized images factory
1406: romizedImageFactory = RomizedImageFactory.getFactory(rawFormat,
1407: colorFormat, endianFormat);
1408:
1409: // assign offsets
1410: assignPropertiesValuesOffsets(intSeqProps, 0);
1411: assignPropertiesValuesOffsets(stringProps, 0);
1412: assignPropertiesValuesOffsets(fontProps, 0);
1413:
1414: // values for composite image properties are stored in same
1415: // array as values for image properties. first, values for
1416: // image properties are stored, followed by values for
1417: // composite image properties.
1418: int lastOffset = assignPropertiesValuesOffsets(imageProps, 0);
1419: ImageSkinProperty.totalImages = lastOffset - 1;
1420: lastOffset = assignPropertiesValuesOffsets(compImageProps,
1421: lastOffset);
1422: CompositeImageSkinProperty.totalImages = lastOffset - 1;
1423:
1424: // total unique skin images
1425: int totalImages = ImageSkinProperty.totalImages
1426: + CompositeImageSkinProperty.totalImages;
1427:
1428: // if image isn't romized, then corresponding value will be null
1429: romizedImages = new Vector(totalImages);
1430: romizedImages.setSize(totalImages);
1431:
1432: romizeImages();
1433:
1434: // output generated file
1435: makeDirectoryTree(romizationJob.outBinFileName);
1436:
1437: OutputStream out = new BufferedOutputStream(
1438: new FileOutputStream(romizationJob.outBinFileName),
1439: 8192);
1440: outputStream = new BinaryOutputStream(
1441: out,
1442: endianFormat == ImageToRawConverter.INT_FORMAT_BIG_ENDIAN);
1443:
1444: writeBinHeader();
1445: writeRomizedProperties();
1446: outputStream.close();
1447:
1448: out = new FileOutputStream(romizationJob.outCFileName);
1449: writer = new PrintWriter(new OutputStreamWriter(out));
1450:
1451: writeCHeader();
1452: writeRomizedImagesData();
1453: writeGetMethod();
1454: writer.close();
1455: }
1456:
1457: /**
1458: * Walks the XML tree and collects all skin properties elements
1459: *
1460: * @param n current DOM node in document
1461: * @exception Exception if there was an error during romization
1462: */
1463: private void collectSkinProperties(Node n) throws Exception {
1464:
1465: if (n.getNodeName().equals("skin") && (n instanceof Element)) {
1466: NodeList list = n.getChildNodes();
1467: for (int i = 0; i < list.getLength(); i++) {
1468: collectSkinProperties(list.item(i));
1469: }
1470: } else if (n.getNodeName().equals("rawimage")
1471: && (n instanceof Element)) {
1472:
1473: collectImageFormat(n);
1474: } else if (n.getNodeName().equals("skin_properties")
1475: && (n instanceof Element)) {
1476:
1477: NodeList list = n.getChildNodes();
1478: for (int i = 0; i < list.getLength(); i++) {
1479: collectSkinProperty(list.item(i));
1480: }
1481: } else {
1482: NodeList list = n.getChildNodes();
1483: for (int i = 0; i < list.getLength(); i++) {
1484: collectSkinProperties(list.item(i));
1485: }
1486: }
1487: }
1488:
1489: /**
1490: * Collects raw images format specification
1491: * @param n current DOM node in document
1492: * @exception Exception if there was an error during romization
1493: */
1494: private void collectImageFormat(Node n) {
1495: Element e = (Element) n;
1496:
1497: String rawFormatStr = e.getAttribute("Format");
1498: rawFormat = ImageToRawConverter.FORMAT_INVALID;
1499: for (int i = 0; i < imageFormatStrings.length; ++i) {
1500: if (imageFormatStrings[i].equals(rawFormatStr)) {
1501: rawFormat = imageFormats[i];
1502: break;
1503: }
1504: }
1505: if (rawFormat == ImageToRawConverter.FORMAT_INVALID) {
1506: throw new IllegalArgumentException(
1507: "invalid raw file format " + '"' + rawFormatStr
1508: + '"');
1509: }
1510:
1511: String colorFormatStr = e.getAttribute("Colors");
1512: colorFormat = ImageToRawConverter.FORMAT_INVALID;
1513: for (int i = 0; i < imageFormatStrings.length; ++i) {
1514: if (imageFormatStrings[i].equals(colorFormatStr)) {
1515: colorFormat = imageFormats[i];
1516: break;
1517: }
1518: }
1519: if (colorFormat == ImageToRawConverter.FORMAT_INVALID) {
1520: throw new IllegalArgumentException("invalid color format "
1521: + '"' + colorFormatStr + '"');
1522: }
1523:
1524: String endianFormatStr = e.getAttribute("Endian");
1525: endianFormat = ImageToRawConverter.FORMAT_INVALID;
1526: for (int i = 0; i < imageFormatStrings.length; ++i) {
1527: if (imageFormatStrings[i].equals(endianFormatStr)) {
1528: endianFormat = imageFormats[i];
1529: break;
1530: }
1531: }
1532: if (endianFormat == ImageToRawConverter.FORMAT_INVALID) {
1533: throw new IllegalArgumentException("invalid color format "
1534: + '"' + endianFormatStr + '"');
1535: }
1536:
1537: if (!ImageToRawConverter.isFormatSupported(rawFormat,
1538: colorFormat)) {
1539: throw new IllegalArgumentException(
1540: "unsupported romized image format: " + "raw file "
1541: + '"' + rawFormat + '"' + ", color " + '"'
1542: + colorFormat + '"');
1543: }
1544: }
1545:
1546: /**
1547: * Collects single skin property element.
1548: *
1549: * @param n DOM node corresponding to skin property description
1550: * in XML file
1551: * @exception Exception if there was an error during romization
1552: */
1553: private void collectSkinProperty(Node n) throws Exception {
1554:
1555: if (n instanceof Element) {
1556: SkinPropertyBase p = SkinPropertyBase.valueOf(n);
1557:
1558: // ID of the property is the index into skinProperties vector
1559: if (allProps.size() < p.id + 1) {
1560: allProps.setSize(p.id + 1);
1561: }
1562:
1563: if (allProps.elementAt(p.id) != null) {
1564: throw new IllegalArgumentException(
1565: "Duplicate skin property " + p.idName);
1566: }
1567: allProps.setElementAt(p, p.id);
1568:
1569: if (p instanceof IntSeqSkinProperty) {
1570: intSeqProps.add(p);
1571: } else if (p instanceof StringSkinProperty) {
1572: stringProps.add(p);
1573: } else if (p instanceof FontSkinProperty) {
1574: fontProps.add(p);
1575: } else if (p instanceof ImageSkinProperty) {
1576: ImageSkinProperty ip = (ImageSkinProperty) p;
1577: if (romizationJob.imageRomOverride.equals("all")) {
1578: ip.isRomized = true;
1579: } else if (romizationJob.imageRomOverride
1580: .equals("none")) {
1581: ip.isRomized = false;
1582: }
1583:
1584: imageProps.add(p);
1585: } else if (p instanceof CompositeImageSkinProperty) {
1586: CompositeImageSkinProperty ip = (CompositeImageSkinProperty) p;
1587: if (romizationJob.imageRomOverride.equals("all")) {
1588: ip.isRomized = true;
1589: } else if (romizationJob.imageRomOverride
1590: .equals("none")) {
1591: ip.isRomized = false;
1592: }
1593:
1594: compImageProps.add(p);
1595: }
1596: }
1597: }
1598:
1599: /**
1600: * Assigns offsets into properties values array
1601: *
1602: * @param props properties to assign offsets for
1603: * @param startOffset offset to start from
1604: * @return offset past the last property value
1605: */
1606: private static int assignPropertiesValuesOffsets(Vector props,
1607: int startOffset) {
1608:
1609: // current (last) offset in the values array
1610: int curOffset = startOffset;
1611:
1612: for (int i = 0; i < props.size(); ++i) {
1613: SkinPropertyBase curP = (SkinPropertyBase) props
1614: .elementAt(i);
1615:
1616: // properties with same values share same offset,
1617: // so look if there already was property with same value
1618: int sameValueOffset = -1;
1619: for (int j = i - 1; j >= 0; --j) {
1620: SkinPropertyBase p = (SkinPropertyBase) props
1621: .elementAt(j);
1622: if (p.isEqualValue(curP)) {
1623: sameValueOffset = p.valueOffset;
1624: break;
1625: }
1626: }
1627:
1628: if (sameValueOffset != -1) {
1629: // use offset from property with same value
1630: curP.valueOffset = sameValueOffset;
1631: } else {
1632: // this value is new, give it current offset
1633: curP.valueOffset = curOffset;
1634: curOffset += curP.getValueOffsetDelta();
1635: }
1636: }
1637:
1638: return curOffset;
1639: }
1640:
1641: /**
1642: * Romizes images
1643: *
1644: * @exception IOException if there was IO error during romization
1645: */
1646: private void romizeImages() throws IOException {
1647:
1648: int maxOffset = -1;
1649: for (int i = 0; i < imageProps.size(); ++i) {
1650: ImageSkinProperty p = (ImageSkinProperty) imageProps
1651: .elementAt(i);
1652: if (!(p.isRomized) || !p.hasValue) {
1653: continue;
1654: }
1655:
1656: // this property has the same value as some other
1657: // property seen before, so skip it
1658: if (p.valueOffset <= maxOffset) {
1659: continue;
1660: }
1661:
1662: romizeImage(p.value, p.valueOffset);
1663: maxOffset = p.valueOffset;
1664: }
1665:
1666: for (int i = 0; i < compImageProps.size(); ++i) {
1667: CompositeImageSkinProperty p = (CompositeImageSkinProperty) compImageProps
1668: .elementAt(i);
1669: if (!(p.isRomized) || !p.hasValue) {
1670: continue;
1671: }
1672:
1673: // this property has the same value as some other
1674: // property seen before, so skip it
1675: if (p.valueOffset <= maxOffset) {
1676: continue;
1677: }
1678:
1679: for (int j = 0; j < p.value.length; ++j) {
1680: romizeImage(p.value[j], p.valueOffset + j);
1681: }
1682: maxOffset = p.valueOffset;
1683: }
1684:
1685: }
1686:
1687: /**
1688: * Romizes single image
1689: *
1690: * @param imageName of the image to romize without extension
1691: * @param imageIndex romized image index
1692: * @exception IOException if there was IO error during romization
1693: */
1694: private void romizeImage(String imageName, int imageIndex)
1695: throws IOException {
1696:
1697: // we romize png images only
1698: String imageFileName = imageName + ".png";
1699: imageFileName = romizationJob.skinImagesDirName
1700: + File.separator + imageFileName;
1701:
1702: System.out.println(" " + imageFileName);
1703:
1704: // load image
1705: BufferedImage image = javax.imageio.ImageIO.read(new File(
1706: imageFileName));
1707:
1708: // and romize it
1709: RomizedImage ri = romizedImageFactory.createFromBufferedImage(
1710: image, imageIndex);
1711:
1712: romizedImages.set(imageIndex, ri);
1713: }
1714:
1715: /**
1716: * Writes copyrigth banner
1717: */
1718: private void writeCopyright() {
1719: pl("/**");
1720: pl(" * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.");
1721: pl(" * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER");
1722: pl(" * ");
1723: pl(" * This program is free software; you can redistribute it and/or");
1724: pl(" * modify it under the terms of the GNU General Public License version");
1725: pl(" * 2 only, as published by the Free Software Foundation.");
1726: pl(" * ");
1727: pl(" * This program is distributed in the hope that it will be useful, but");
1728: pl(" * WITHOUT ANY WARRANTY; without even the implied warranty of");
1729: pl(" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU");
1730: pl(" * General Public License version 2 for more details (a copy is");
1731: pl(" * included at /legal/license.txt).");
1732: pl(" * ");
1733: pl(" * You should have received a copy of the GNU General Public License");
1734: pl(" * version 2 along with this work; if not, write to the Free Software");
1735: pl(" * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA");
1736: pl(" * 02110-1301 USA");
1737: pl(" * ");
1738: pl(" * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa");
1739: pl(" * Clara, CA 95054 or visit www.sun.com if you need additional");
1740: pl(" * information or have any questions.");
1741: pl(" * ");
1742: pl(" * NOTE: DO NOT EDIT. THIS FILE IS GENERATED. If you want to ");
1743: pl(" * edit it, you need to modify the corresponding XML files.");
1744: pl(" */");
1745: }
1746:
1747: /**
1748: * Writes RomizedSkin class file header
1749: */
1750: private void writeBinHeader() throws IOException {
1751:
1752: // write magic sequence
1753: int magicLength = SkinResourcesConstants.CHAM_BIN_MAGIC.length;
1754: for (int i = 0; i < magicLength; ++i) {
1755: byte b = (byte) (SkinResourcesConstants.CHAM_BIN_MAGIC[i] & 0xFF);
1756: outputStream.writeByte(b);
1757: }
1758:
1759: // write version info as an array
1760: outputStream.writeInt(1); // array size
1761: outputStream
1762: .writeInt(SkinResourcesConstants.CHAM_BIN_FORMAT_VERSION);
1763: }
1764:
1765: /**
1766: * Writes romized images C file header
1767: */
1768: private void writeCHeader() {
1769: writeCopyright();
1770:
1771: pl("");
1772: pl("#include <string.h>");
1773: pl("#include <kni.h>");
1774: pl("#include <midpError.h>");
1775: pl("#include <midpMalloc.h>");
1776: pl("#include <midpServices.h>\n");
1777: }
1778:
1779: /**
1780: * Writes romized properties data
1781: * @exception Exception if there was an error during output
1782: */
1783: private void writeRomizedProperties() throws Exception {
1784:
1785: outputStream.writeInt(allProps.size());
1786: for (int i = 0; i < allProps.size(); ++i) {
1787: SkinPropertyBase p = (SkinPropertyBase) allProps
1788: .elementAt(i);
1789: if (p instanceof IntSkinProperty) {
1790: IntSkinProperty intP = (IntSkinProperty) p;
1791: // for integer property the array holds actual property's
1792: // value, not an offset into values array
1793: p.outputValue(outputStream);
1794: } else {
1795: // for all other properties the array holds an offset into
1796: // values array, in which actual values are stored
1797: outputStream.writeInt(p.valueOffset);
1798: }
1799: }
1800:
1801: writePropertiesValues(intSeqProps);
1802: writePropertiesValues(stringProps);
1803: writePropertiesValues(fontProps);
1804:
1805: Vector v = new Vector(imageProps);
1806: v.addAll(compImageProps);
1807: writePropertiesValues(v);
1808:
1809: writeRomizedImagesIndexes();
1810: }
1811:
1812: /**
1813: * Writes properties values array entries
1814: *
1815: * @param props properties to write entries for
1816: */
1817: private void writePropertiesValues(Vector props)
1818: throws java.io.IOException {
1819:
1820: int maxOffset = -1;
1821: int totalValues = 0;
1822: for (int i = 0; i < props.size(); ++i) {
1823: SkinPropertyBase p = (SkinPropertyBase) props.elementAt(i);
1824:
1825: // this property has the same value as some other
1826: // property printed before, so skip it
1827: if (p.valueOffset <= maxOffset) {
1828: continue;
1829: }
1830:
1831: totalValues += p.getValueOffsetDelta();
1832: maxOffset = p.valueOffset;
1833: }
1834: outputStream.writeInt(totalValues);
1835:
1836: maxOffset = -1;
1837: for (int i = 0; i < props.size(); ++i) {
1838: SkinPropertyBase p = (SkinPropertyBase) props.elementAt(i);
1839:
1840: // this property has the same value as some other
1841: // property outputed before, so skip it
1842: if (p.valueOffset <= maxOffset) {
1843: continue;
1844: }
1845:
1846: p.outputValue(outputStream);
1847: maxOffset = p.valueOffset;
1848: }
1849: }
1850:
1851: /**
1852: * Writes indexes for romized images.
1853: */
1854: private void writeRomizedImagesIndexes() throws java.io.IOException {
1855:
1856: outputStream.writeInt(romizedImages.size());
1857: for (int i = 0; i < romizedImages.size(); ++i) {
1858: Object o = romizedImages.elementAt(i);
1859: // image with index = i isn't romized
1860: if (o == null) {
1861: outputStream.writeInt(-1);
1862: } else {
1863: RomizedImage ri = (RomizedImage) o;
1864: outputStream.writeInt(ri.imageIndex);
1865: }
1866: }
1867: }
1868:
1869: /**
1870: * Writes romized images data
1871: */
1872: private void writeRomizedImagesData() {
1873: int totalRomizedImages = 0;
1874: for (int i = 0; i < romizedImages.size(); ++i) {
1875: if (romizedImages.elementAt(i) != null) {
1876: ++totalRomizedImages;
1877: }
1878: }
1879:
1880: pl("");
1881: pl("static const int NUM_ROM_IMAGES = " + totalRomizedImages
1882: + ";");
1883:
1884: if (totalRomizedImages != 0) {
1885: // output a structure declaration used for storing
1886: // romized images data in it
1887: pl("");
1888: pl("struct romized_images_data {");
1889: for (int i = 0; i < romizedImages.size(); ++i) {
1890: Object o = romizedImages.elementAt(i);
1891: if (o != null) {
1892: RomizedImage ri = (RomizedImage) o;
1893:
1894: // this field ensures proper alignment of
1895: // subsequent data array
1896: pl(" " + "const int align_" + ri.imageIndex
1897: + ";");
1898:
1899: String dataArrayName = "romized_image"
1900: + ri.imageIndex;
1901: int dataArrayLength = ri.imageData.length;
1902: pl(" " + "const unsigned char " + dataArrayName
1903: + "[" + dataArrayLength + "];");
1904: }
1905: }
1906: pl("};");
1907:
1908: pl("");
1909: pl("static const struct romized_images_data "
1910: + "romized_images_data = {");
1911: for (int i = 0; i < romizedImages.size(); ++i) {
1912: Object o = romizedImages.elementAt(i);
1913: if (o != null) {
1914: // alignemnt field
1915: pl(" " + "0,");
1916:
1917: RomizedImage ri = (RomizedImage) o;
1918: String dataArrayName = "romized_image"
1919: + ri.imageIndex;
1920:
1921: // romized image data field
1922: pl(" " + "/* " + dataArrayName + " */");
1923: pl(" " + "{");
1924: ri.printDataArray(writer, " ", 11);
1925: pl(" },");
1926: }
1927: }
1928: pl("};");
1929: }
1930:
1931: pl("");
1932: pl("static const unsigned char* image_cache[] = {");
1933: // if there are no romized images, print dummy value
1934: // to make array non empty and keep compilers happy
1935: if (totalRomizedImages == 0) {
1936: pl(" NULL");
1937: } else {
1938: for (int i = 0; i < romizedImages.size(); ++i) {
1939: Object o = romizedImages.elementAt(i);
1940: if (o != null) {
1941: RomizedImage ri = (RomizedImage) o;
1942: String dataArrayName = "romized_images_data.romized_image"
1943: + ri.imageIndex;
1944: pl(" " + dataArrayName + ",");
1945: }
1946: }
1947: }
1948: pl("};");
1949:
1950: pl("");
1951: pl("static const int image_size[] = {");
1952: // if there are no romized images, print dummy value
1953: // to make array non empty and keep compilers happy
1954: if (totalRomizedImages == 0) {
1955: pl(" 0");
1956: } else {
1957: for (int i = 0; i < romizedImages.size(); ++i) {
1958: Object o = romizedImages.elementAt(i);
1959: if (o != null) {
1960: RomizedImage ri = (RomizedImage) o;
1961: String dataArrayName = "romized_images_data.romized_image"
1962: + ri.imageIndex;
1963: pl(" sizeof(" + dataArrayName + "),");
1964: }
1965: }
1966: }
1967: pl("};");
1968: }
1969:
1970: /**
1971: * Writes get method for obtaining romized image data
1972: */
1973: void writeGetMethod() {
1974: pl("");
1975: pl("/**");
1976: pl(" * Loads a native image from rom, if present.");
1977: pl(" *");
1978: pl(" * @param imageId The image id");
1979: pl(" * @param **bufPtr Pointer where a buffer will be "
1980: + "allocated and data stored");
1981: pl(" * @return -1 if failed, else length of buffer");
1982: pl(" */");
1983: pl("int lfj_load_image_from_rom(int imageId, "
1984: + "unsigned char** bufPtr) {\n");
1985: pl(" int len = -1;");
1986: pl(" if ((imageId < 0) || (imageId > NUM_ROM_IMAGES)) {");
1987: pl(" REPORT_WARN1(LC_LOWUI,");
1988: pl(" \"Warning: could not load romized"
1989: + "image for index %d; \", imageId); ");
1990: pl(" return len;");
1991: pl(" }\n");
1992: pl(" *bufPtr = (unsigned char*)image_cache[imageId];");
1993: pl(" len = image_size[imageId];");
1994: pl(" return len;");
1995: pl("}");
1996: }
1997:
1998: /**
1999: * Creates a directory structure.
2000: *
2001: * @param fullFileName Full path to the file to be created. If directory
2002: * in which file is to be created doesn't exists, it will be created
2003: * @exception IOException is thrown if directory couldn't be created
2004: */
2005: private void makeDirectoryTree(String fullFileName)
2006: throws IOException {
2007:
2008: if (debug == true) {
2009: System.out.println("mkdir: " + fullFileName);
2010: }
2011: int index = fullFileName.lastIndexOf(File.separatorChar);
2012: if (index == -1) {
2013: // To be compatible with MKS-hosted build on win32, which
2014: // does not translate / to \.
2015: index = fullFileName.lastIndexOf('/');
2016: }
2017: File outputDirectory = new File(fullFileName
2018: .substring(0, index));
2019:
2020: if (!(outputDirectory).exists()) {
2021: if (!(outputDirectory).mkdirs()) {
2022: throw new IOException(
2023: "failed to create output directory");
2024: }
2025: }
2026:
2027: }
2028:
2029: /**
2030: * Short-hand for printint a line into the output file
2031: *
2032: * @param s line to print
2033: */
2034: void pl(String s) {
2035: writer.println(s);
2036: }
2037: }
|