001: /*
002: * Copyright (c) 2007, Sun Microsystems, Inc.
003: *
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * * Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: * * Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: * * Neither the name of Sun Microsystems, Inc. nor the names of its
017: * contributors may be used to endorse or promote products derived
018: * from this software without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
023: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
024: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031: */
032: package example.mmademo;
033:
034: import javax.microedition.midlet.*;
035: import javax.microedition.lcdui.*;
036: import javax.microedition.media.*;
037: import javax.microedition.media.control.*;
038: import java.util.*;
039: import java.io.*;
040:
041: /**
042: * Utility functions and listener interfaces
043: *
044: * @version 1.5
045: */
046: public class Utils {
047: public static boolean DEBUG = false;
048:
049: private Utils() {
050: //prevent accidental instantiation
051: }
052:
053: public static void debugOut(String s) {
054: if (DEBUG)
055: System.out.println(s);
056: }
057:
058: public static void debugOut(Throwable t) {
059: if (DEBUG)
060: System.out.println(t.toString());
061: if (DEBUG)
062: t.printStackTrace();
063: }
064:
065: public static void error(Throwable t, BreadCrumbTrail bct) {
066: if (DEBUG)
067: t.printStackTrace();
068: error(friendlyException(t), bct);
069: }
070:
071: public static void error(String s, BreadCrumbTrail bct) {
072: Alert alert = new Alert("Error", s, null, AlertType.ERROR);
073: alert.setTimeout(Alert.FOREVER);
074: bct.replaceCurrent(alert);
075: }
076:
077: public static void FYI(String s, BreadCrumbTrail bct) {
078: Alert alert = new Alert("FYI", s, null, AlertType.INFO);
079: alert.setTimeout(Alert.FOREVER);
080: bct.replaceCurrent(alert);
081: }
082:
083: /**
084: * "javax.microedition.rms.RecordStoreException: Bla"
085: * ->
086: * "RecordStoreException: Bla"
087: */
088: public static String friendlyException(Throwable t) {
089: if (t instanceof MediaException
090: && t.getMessage().indexOf(" ") > 5) {
091: return t.getMessage();
092: }
093: String s = t.toString();
094: while (true) {
095: int dot = s.indexOf(".");
096: int space = s.indexOf(" ");
097: if (space < 0)
098: space = s.length();
099: int colon = s.indexOf(":");
100: if (colon < 0)
101: colon = s.length();
102: if (dot >= 0 && dot < space && dot < colon) {
103: s = s.substring(dot + 1);
104: } else {
105: break;
106: }
107: }
108: return s;
109: }
110:
111: /**
112: * Prompts the user to enter a string.<p>
113: * <b>Caution</b>: this must be called in
114: * a different Thread than the lcdui event dispatcher. Unfortunately !
115: *
116: * @param title - title of the query window
117: * @param def - a default value that appears in the input field
118: * @param maxSize - max number of characters
119: * @return the entered text, or <code>null</code> if the user cancelled
120: */
121: public static void query(String title, String def, int maxSize,
122: QueryListener listener, BreadCrumbTrail bct) {
123: query(title, def, maxSize, TextField.ANY, listener, bct);
124: }
125:
126: /**
127: * Prompts the user to enter a string. <p>
128: * When the user finished entering the text,
129: * the <code>listener</code>'s methods are called.
130: * If the user pressed the OK button, the
131: * <code>listener</code>'s <code>queryOK</code> method
132: * is called with the entered text as parameter.
133: *
134: * The <code>listener</code> need not care about hiding the displayable.
135: *
136: * @param title - title of the query window
137: * @param def - a default value that appears in the input field
138: * @param maxSize - max number of characters
139: * @param constraints - see javax.microedition.lcdui.TextBox
140: * @param listener - the QueryListener which receives the events
141: */
142: public static void query(String title, String def, int maxSize,
143: int constraints, QueryListener listener, BreadCrumbTrail bct) {
144: TextBox tb = new TextBox(title, def, maxSize, constraints);
145: tb.addCommand(QueryTask.cancelCommand);
146: tb.addCommand(QueryTask.OKCommand);
147: QueryTask qt = new QueryTask(listener, bct);
148: tb.setCommandListener(qt);
149: bct.go(tb);
150: }
151:
152: /**
153: * splits the URL in the parts
154: * E.g: http://www.12fb.com:80/Media/MIDI/fb.mid#1
155: *
156: * 0: protocol (e.g. http)
157: * 1: host (e.g. www.12fb.com)
158: * 2: port (e.g. 80)
159: * 3: path (e.g. /Media/MIDI)
160: * 4: file (e.g. fb.mid)
161: * 5: anchor (e.g. 1)
162: *
163: * LIMITATION: URL must end with a slash if it is a directory
164: */
165: public static String[] splitURL(String url) throws Exception {
166: StringBuffer u = new StringBuffer(url);
167: String[] result = new String[6];
168: for (int i = 0; i <= 5; i++) {
169: result[i] = "";
170: }
171: // get protocol
172: boolean protFound = false;
173: int index = url.indexOf(":");
174: if (index > 0) {
175: result[0] = url.substring(0, index);
176: u.delete(0, index + 1);
177: protFound = true;
178: } else if (index == 0) {
179: throw new Exception("url format error - protocol");
180: }
181: // check for host/port
182: if (u.length() > 2 && u.charAt(0) == '/' && u.charAt(1) == '/') {
183: // found domain part
184: u.delete(0, 2);
185: int slash = u.toString().indexOf('/');
186: if (slash < 0) {
187: slash = u.length();
188: }
189: int colon = u.toString().indexOf(':');
190: int endIndex = slash;
191: if (colon >= 0) {
192: if (colon > slash) {
193: throw new Exception("url format error - port");
194: }
195: endIndex = colon;
196: result[2] = u.toString().substring(colon + 1, slash);
197: }
198: result[1] = u.toString().substring(0, endIndex);
199: u.delete(0, slash);
200: }
201: // get filename
202: if (u.length() > 0) {
203: url = u.toString();
204: int slash = url.lastIndexOf('/');
205: if (slash > 0) {
206: result[3] = url.substring(0, slash);
207: }
208: if (slash < url.length() - 1) {
209: String fn = url.substring(slash + 1, url.length());
210: int anchorIndex = fn.indexOf("#");
211: if (anchorIndex >= 0) {
212: result[4] = fn.substring(0, anchorIndex);
213: result[5] = fn.substring(anchorIndex + 1);
214: } else {
215: result[4] = fn;
216: }
217: }
218: }
219: return result;
220: }
221:
222: public static String mergeURL(String[] url) {
223: return ((url[0] == "") ? "" : url[0] + ":/")
224: + ((url[1] == "") ? "" : "/" + url[1])
225: + ((url[2] == "") ? "" : ":" + url[2]) + url[3] + "/"
226: + url[4] + ((url[5] == "") ? "" : "#" + url[5]);
227: }
228:
229: public static String guessContentType(String url) throws Exception {
230: // guess content type
231: String[] sURL = splitURL(url);
232: String ext = "";
233: String ct = "";
234: int lastDot = sURL[4].lastIndexOf('.');
235: if (lastDot >= 0) {
236: ext = sURL[4].substring(lastDot + 1).toLowerCase();
237: }
238: if (ext.equals("mpg") || url.equals("avi")) {
239: ct = "video/mpeg";
240: } else if (ext.equals("mid") || ext.equals("kar")) {
241: ct = "audio/midi";
242: } else if (ext.equals("wav")) {
243: ct = "audio/x-wav";
244: } else if (ext.equals("jts")) {
245: ct = "audio/x-tone-seq";
246: } else if (ext.equals("txt")) {
247: ct = "audio/x-txt";
248: } else if (ext.equals("amr")) {
249: ct = "audio/amr";
250: } else if (ext.equals("awb")) {
251: ct = "audio/amr-wb";
252: } else if (ext.equals("gif")) {
253: ct = "image/gif";
254: }
255: return ct;
256: }
257:
258: /**
259: * From SortDemo - modified to take Strings
260: *
261: * This is a generic version of C.A.R Hoare's Quick Sort
262: * algorithm. This will handle arrays that are already
263: * sorted, and arrays with duplicate keys.<BR>
264: *
265: * If you think of a one dimensional array as going from
266: * the lowest index on the left to the highest index on the right
267: * then the parameters to this function are lowest index or
268: * left and highest index or right. The first time you call
269: * this function it will be with the parameters 0, a.length - 1.
270: *
271: * @param s a String array
272: * @param lo0 left boundary of array partition
273: * @param hi0 right boundary of array partition
274: */
275: private static void quickSort(String[] s, int lo0, int hi0) {
276: int lo = lo0;
277: int hi = hi0;
278: String mid;
279:
280: if (hi0 > lo0) {
281:
282: /* Arbitrarily establishing partition element as the midpoint of
283: * the array.
284: */
285: mid = s[(lo0 + hi0) / 2].toUpperCase();
286:
287: // loop through the array until indices cross
288: while (lo <= hi) {
289: /* find the first element that is greater than or equal to
290: * the partition element starting from the left Index.
291: */
292: while ((lo < hi0)
293: && (s[lo].toUpperCase().compareTo(mid) < 0)) {
294: ++lo;
295: }
296:
297: /* find an element that is smaller than or equal to
298: * the partition element starting from the right Index.
299: */
300: while ((hi > lo0)
301: && (s[hi].toUpperCase().compareTo(mid) > 0)) {
302: --hi;
303: }
304:
305: // if the indexes have not crossed, swap
306: if (lo <= hi) {
307: String temp;
308: temp = s[lo];
309: s[lo] = s[hi];
310: s[hi] = temp;
311: ++lo;
312: --hi;
313: }
314: }
315:
316: /* If the right index has not reached the left side of array
317: * must now sort the left partition.
318: */
319: if (lo0 < hi) {
320: quickSort(s, lo0, hi);
321: }
322:
323: /* If the left index has not reached the right side of array
324: * must now sort the right partition.
325: */
326: if (lo < hi0) {
327: quickSort(s, lo, hi0);
328: }
329:
330: }
331: }
332:
333: public static void sort(String[] elements) {
334: quickSort(elements, 0, elements.length - 1);
335: }
336:
337: /**
338: * A class to handle the query
339: */
340: private static class QueryTask implements CommandListener, Runnable {
341: private static Command cancelCommand = new Command("Cancel",
342: Command.CANCEL, 1);
343: private static Command OKCommand = new Command("OK",
344: Command.OK, 1);
345:
346: // "parameters" passed to commandAction method
347: private QueryListener queryListener;
348: private BreadCrumbTrail queryBCT;
349: private static String queryText = "";
350:
351: private QueryTask(QueryListener listener, BreadCrumbTrail bct) {
352: this .queryListener = listener;
353: this .queryBCT = bct;
354: }
355:
356: /**
357: * Respond to commands
358: */
359: public void commandAction(Command c, Displayable s) {
360: if (queryBCT != null) {
361: Utils.debugOut("Utils.commandAction: goBack()");
362: queryBCT.goBack();
363: }
364: if (c == cancelCommand) {
365: Utils.debugOut("Command: cancel");
366: if (queryListener != null) {
367: queryListener.queryCancelled();
368: }
369: } else if (c == OKCommand) {
370: Utils.debugOut("Command: OK");
371: if (queryListener != null) {
372: queryText = "";
373: if (s instanceof TextBox) {
374: queryText = ((TextBox) s).getString();
375: }
376: // for some reasons, MIDP may have a deadlock
377: // if some lengthy operation (i.e. http i/o)
378: // is initiated from the command listener
379: // thread. Therefore, issue the event
380: // from another thread...
381: (new Thread(this )).start();
382: }
383: }
384: }
385:
386: /**
387: * Runnable implementation -- sends
388: * the event to the listener.
389: * It is executed in a separate thread
390: * to not block the VM's command dispatch
391: * thread.
392: */
393: public void run() {
394: sendListenerEvent();
395: }
396:
397: /**
398: * Send the text to the query listener
399: */
400: private void sendListenerEvent() {
401: if (queryListener != null) {
402: queryListener.queryOK(queryText);
403: }
404: }
405: }
406:
407: /**
408: * Interface to be implemented by classes
409: * that provide <i>Back</i> functionality.
410: */
411: interface BreadCrumbTrail {
412: public Displayable go(Displayable d);
413:
414: public Displayable goBack();
415:
416: public void handle(String name, String url);
417:
418: public Displayable replaceCurrent(Displayable d);
419:
420: public Displayable getCurrentDisplayable();
421: }
422:
423: /**
424: * Interface implemented by classes that
425: * can handle (playback, display, etc.) url's.
426: */
427: interface ContentHandler {
428: public void close();
429:
430: public boolean canHandle(String url);
431:
432: public void handle(String name, String url);
433: }
434:
435: /**
436: * Interface that is implemented by classes
437: * that use the query() functions.
438: */
439: interface QueryListener {
440: public void queryOK(String text);
441:
442: public void queryCancelled();
443: }
444:
445: /**
446: * Interface implemented by Displayable's that
447: * want to respond to the MIDlet's startApp()
448: * and pauseApp() calls.
449: */
450: interface Interruptable {
451: /**
452: * Called in response to a request to pause the MIDlet.
453: */
454: public void pauseApp();
455:
456: /**
457: * Called when a MIDlet is asked to resume operations
458: * after a call to pauseApp(). This method is only
459: * called after pauseApp(), so it is different from
460: * MIDlet's startApp().
461: */
462: public void resumeApp();
463: }
464: }
|