001: /* Copyright 2006-2007 David N. Welton, DedaSys LLC
002:
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014: */
015:
016: package jarhack;
017:
018: import java.io.IOException;
019:
020: import java.io.File;
021: import java.io.FileInputStream;
022: import java.io.FileOutputStream;
023: import java.io.FileWriter;
024: import java.io.InputStream;
025:
026: import java.util.Iterator;
027: import java.util.Set;
028: import java.util.Vector;
029:
030: import java.util.jar.*;
031:
032: /**
033: * <code>JarHack</code> -- this class provides several static methods
034: * that can be used to create .jar and .jad files from a template
035: * Hecl.jar and some user supplied information, such as the output
036: * file, and the name of the new application.
037: *
038: * @author <a href="mailto:davidw@dedasys.com">David N. Welton</a>
039: * @version 1.0
040: */
041: public class JarHack {
042:
043: private static String cldcversion = "";
044: private static String midpversion = "";
045:
046: /**
047: * The <code>substHecl</code> method takes the filenames of two
048: * .jar's - one as input, the second as output, in addition to the
049: * name of the application. Where it counts, the old name (Hecl,
050: * usually) is overridden with the new name, and the new .jar file
051: * is written to the specified outfile.
052: *
053: * @param infile a <code>FileInputStream</code> value
054: * @param outfile a <code>String</code> value
055: * @param newname a <code>String</code> value
056: * @exception IOException if an error occurs
057: */
058: public static void substHecl(InputStream infile, String outfile,
059: String newname, String scriptfile) throws IOException {
060:
061: JarInputStream jif = new JarInputStream(infile);
062: Manifest mf = jif.getManifest();
063: Attributes attrs = mf.getMainAttributes();
064:
065: Set keys = attrs.keySet();
066: Iterator it = keys.iterator();
067: while (it.hasNext()) {
068: Object key = it.next();
069: Object value = attrs.get(key);
070: String keyname = key.toString();
071:
072: /* These are the three cases that interest us in
073: * particular, where we need to make changes. */
074: if (keyname.equals("MIDlet-Name")) {
075: attrs.putValue(keyname, newname);
076: } else if (keyname.equals("MIDlet-1")) {
077: String valuestr = value.toString();
078: /* FIXME - the stringsplit method is used for older
079: * versions of GCJ. Once newer versions are common,
080: * it can go away. Or not - it works just fine. */
081: String properties[] = stringsplit(valuestr, ", ");
082: attrs.putValue(keyname, newname + ", " + properties[1]
083: + ", " + properties[2]);
084: } else if (keyname.equals("MicroEdition-Configuration")) {
085: cldcversion = value.toString();
086: } else if (keyname.equals("MicroEdition-Profile")) {
087: midpversion = value.toString();
088: } else if (keyname.equals("MIDlet-Jar-URL")) {
089: attrs.put(key, newname + ".jar");
090: }
091: }
092:
093: JarOutputStream jof = new JarOutputStream(new FileOutputStream(
094: outfile), mf);
095:
096: byte[] buf = new byte[4096];
097:
098: /* Go through the various entries. */
099: JarEntry entry;
100: int read;
101: while ((entry = jif.getNextJarEntry()) != null) {
102:
103: /* Don't copy the manifest file. */
104: if ("META-INF/MANIFEST.MF".equals(entry.getName()))
105: continue;
106:
107: /* Insert our own copy of the script file. */
108: if ("script.hcl".equals(entry.getName())) {
109: jof.putNextEntry(new JarEntry("script.hcl"));
110: FileInputStream inf = new FileInputStream(scriptfile);
111: while ((read = inf.read(buf)) != -1) {
112: jof.write(buf, 0, read);
113: }
114: inf.close();
115: } else {
116: /* Otherwise, just copy the entry. */
117: jof.putNextEntry(entry);
118: while ((read = jif.read(buf)) != -1) {
119: jof.write(buf, 0, read);
120: }
121: }
122:
123: jof.closeEntry();
124: }
125:
126: jof.flush();
127: jof.close();
128: jif.close();
129: }
130:
131: /**
132: * The <code>createJadForJar</code> method calls createJadForJar
133: * with a url created from the application's name.
134: *
135: * @param jarfile a <code>String</code> value
136: * @param appname a <code>String</code> value
137: * @exception IOException if an error occurs
138: */
139: public static void createJadForJar(String jarfile, String appname)
140: throws IOException {
141: String url = appname + ".jar";
142: createJadForJar(jarfile, appname, url);
143: }
144:
145: /**
146: * The <code>createJadForJar</code> method creates a new .jad file
147: * that matches the .jar file passed to it. appname is the name
148: * of the new application.
149: *
150: * @param jarfile a <code>String</code> value
151: * @param appname a <code>String</code> value
152: * @param url a <code>String</code> value
153: * @exception IOException if an error occurs
154: */
155: public static void createJadForJar(String jarfile, String appname,
156: String url) throws IOException {
157: File jf = new File(jarfile);
158: String parent = jf.getParent();
159: File jadfile = new File(parent + File.separatorChar + appname
160: + ".jad");
161: FileWriter of = new FileWriter(jadfile);
162: of.write("MIDlet-1: " + appname + ", Hecl.png, Hecl" + "\n"
163: + "MIDlet-Info-URL: http://www.hecl.org" + "\n"
164: + "MIDlet-Jar-Size: " + jf.length() + "\n"
165: + "MIDlet-Jar-URL: " + url + "\n" + "MIDlet-Name: "
166: + appname + "\n" + "MIDlet-Vendor: dedasys.com" + "\n"
167: + "MIDlet-Version: 1.1" + "\n"
168: + "MicroEdition-Profile: " + midpversion + "\n"
169: + "MicroEdition-Configuration: " + cldcversion);
170: of.close();
171: }
172:
173: /**
174: * <code>main</code> implements a command line version of JarHack
175: * that takes five arguments: 1. The original 'template' .jar
176: * file to use. 2. The destination directory where you want to
177: * create your new application. 3. The name of your new
178: * application as it will appear in the MIDlet. 4. The name of
179: * the Hecl script you want to use instead of the script.hcl that
180: * is included in the 'source' .jar file. 5. The URL where your
181: * jar file will reside.
182: *
183: * @param args a <code>String[]</code> value
184: */
185: public static void main(String[] args) {
186: if (args.length < 4 || args.length > 5) {
187: System.err
188: .println("Usage: JarHack original.jar destination_dir ApplicationName yourscript.hcl ?url?");
189: System.exit(1);
190: }
191:
192: String sourcefile = args[0];
193: String destdir = args[1];
194: String appname = args[2];
195: String scriptfile = args[3];
196: String url;
197: if (args.length == 5) {
198: url = args[4];
199: } else {
200: url = appname + ".jar";
201: }
202:
203: if (!(new File(destdir)).isDirectory()) {
204: System.err.println("Not a valid directory: " + destdir);
205: System.exit(1);
206: }
207:
208: String newfn = destdir + appname + ".jar";
209:
210: try {
211: FileInputStream infile = new FileInputStream(sourcefile);
212: substHecl(infile, newfn, appname, scriptfile);
213: createJadForJar(newfn, appname, url);
214: } catch (Exception e) {
215: System.err.println("Error: " + e);
216: e.printStackTrace();
217: }
218: }
219:
220: /**
221: * The <code>stringsplit</code> method is here because older
222: * versions of GCJ don't seem to handle String.split very well.
223: *
224: * @param in a <code>String</code> value
225: * @param split a <code>String</code> value
226: * @return a <code>String[]</code> value
227: */
228: private static String[] stringsplit(String in, String split) {
229: Vector ret = new Vector();
230: int fromindex = 0;
231: int idx = 0;
232: int splitlen = split.length();
233: while (true) {
234: idx = in.indexOf(split, fromindex);
235: if (idx < 0)
236: break;
237: ret.addElement(in.substring(fromindex, idx));
238: fromindex = idx + splitlen;
239: }
240: ret.addElement(in.substring(fromindex));
241: String[] retstr = new String[ret.size()];
242: ret.copyInto(retstr);
243: return retstr;
244: }
245: }
|