--- /dev/null
+// SampleLabel.java\r
+// $Id: SampleLabel.java,v 1.1 2010/06/15 12:25:29 smhuang Exp $\r
+// (c) COPYRIGHT MIT and INRIA, 1996.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+\r
+package org.w3c.jigsaw.pics ;\r
+\r
+import java.io.BufferedInputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.LineNumberInputStream;\r
+import java.io.PrintStream;\r
+import java.io.StringBufferInputStream;\r
+\r
+// I like writing these silly kind of parsers !\r
+// (its probably the X)\r
+\r
+class LabelParser { \r
+ File file = null ;\r
+ LineNumberInputStream in = null ;\r
+ int ch = -1 ;\r
+ byte buffer[] = null ;\r
+ int bufptr = 0 ;\r
+\r
+ /**\r
+ * Append the given char in the internal buffer\r
+ */\r
+\r
+ public void append (int ch) {\r
+ if ( bufptr + 1 >= buffer.length) {\r
+ // resize buffer \r
+ byte nbuf[] = new byte[buffer.length * 2] ;\r
+ System.arraycopy (buffer, 0, nbuf, 0, bufptr) ;\r
+ buffer = nbuf ;\r
+ }\r
+ buffer[bufptr++] = (byte) ch ;\r
+ }\r
+\r
+ /**\r
+ * Get the token from our internale buffer.\r
+ */\r
+\r
+ public String getToken (boolean clear) {\r
+ String tok = new String (buffer, 0, 0, bufptr) ;\r
+ if ( clear )\r
+ bufptr = 0 ;\r
+ return tok ;\r
+ }\r
+\r
+ /**\r
+ * Parser expects given character.\r
+ */\r
+\r
+ void expect (int c) \r
+ throws InvalidLabelException\r
+ {\r
+ if ( ch == c )\r
+ return ;\r
+ String msg = ("expected " \r
+ + (new Character((char) c)).toString()\r
+ + "[" + c + "]"\r
+ + " got " \r
+ + (new Character((char) ch)).toString() \r
+ + "[" + ch + "]");\r
+ if (file == null)\r
+ throw new InvalidLabelException(in.getLineNumber(), msg) ;\r
+ else\r
+ throw new InvalidLabelFileException (file, in.getLineNumber(), msg) ;\r
+ }\r
+\r
+ String parseVariableName () \r
+ throws IOException\r
+ {\r
+ while ((ch != '=') && (ch != '\n') && (ch != -1)) {\r
+ append (Character.toLowerCase((char)ch)) ;\r
+ ch = in.read() ;\r
+ }\r
+ return getToken(true) ;\r
+ }\r
+\r
+ String parseVariableValue() \r
+ throws IOException\r
+ {\r
+ while ( (ch != -1) && (ch != '\n') ) {\r
+ append (ch) ;\r
+ ch = in.read() ;\r
+ }\r
+ return getToken(true) ;\r
+ }\r
+\r
+ SampleLabel parse (SampleLabel into)\r
+ throws InvalidLabelException, InvalidLabelFileException\r
+ {\r
+ try {\r
+ while ( true ) {\r
+ switch (ch) {\r
+ case -1:\r
+ // we are done.\r
+ return into ;\r
+ case ' ': case '\t': case '\n':\r
+ ch = in.read() ;\r
+ continue ;\r
+ case '#':\r
+ while ( ((ch = in.read()) != '\n') && (ch != -1) )\r
+ ;\r
+ continue ;\r
+ default:\r
+ String name = parseVariableName() ;\r
+ expect ('=') ; ch = in.read() ;\r
+ String value = parseVariableValue() ;\r
+ if (ch != -1) { // Pb -1 instead of \n\r
+ expect ('\n') ; \r
+ ch = in.read() ;\r
+ }\r
+ into.addBinding (name, value) ;\r
+ continue ;\r
+ }\r
+ }\r
+ } catch (IOException e) {\r
+ if (file == null)\r
+ throw new InvalidLabelException( "IO exception.") ;\r
+ else\r
+ throw new InvalidLabelFileException (file.getAbsolutePath()\r
+ + ": IO exception.") ;\r
+ }\r
+ }\r
+\r
+ LabelParser (File file) \r
+ throws InvalidLabelFileException\r
+ {\r
+ this.file = file ;\r
+ try {\r
+ this.in = (new LineNumberInputStream \r
+ (new BufferedInputStream\r
+ (new FileInputStream (file)))) ;\r
+ this.buffer = new byte[32] ;\r
+ this.ch = in.read() ;\r
+ } catch (IOException e) {\r
+ throw new InvalidLabelFileException (file.getAbsolutePath()\r
+ + ": IO exception.") ;\r
+ }\r
+ }\r
+\r
+ LabelParser (String string) \r
+ throws InvalidLabelException\r
+ {\r
+ try {\r
+ this.in = (new LineNumberInputStream \r
+ (new BufferedInputStream\r
+ (new StringBufferInputStream (string)))) ;\r
+\r
+ this.buffer = new byte[32] ;\r
+ this.ch = in.read() ;\r
+ } catch (IOException e) {\r
+ throw new InvalidLabelException( "IO exception.") ;\r
+ }\r
+ }\r
+\r
+}\r
+\r
+/**\r
+ * Label internal representation.\r
+ * The server has to know something about labels. In this implementation, I try\r
+ * to reduce this knowledge as fas as I could. Here, a label is a set of\r
+ * assignements to variables (options in PICS terms), and a rating.\r
+ * The syntax for writing label files is the following:\r
+ * <variable>=<value>\n\r
+ * <p>Comments are allowed through the '#' at beginning of line.\r
+ * With the special variable 'ratings' being mandatory.\r
+ */\r
+\r
+public class SampleLabel implements LabelInterface {\r
+ // Default variables array size\r
+ private static final int VARSIZE = 8 ;\r
+\r
+ String vars[] = null ;\r
+ String vals[] = null ;\r
+\r
+ int varptr = 0 ;\r
+\r
+ void addBinding (String var, String val) {\r
+ if ( varptr + 1 >= vars.length ) {\r
+ // resize arrays \r
+ String nvars[] = new String[vars.length*2] ;\r
+ String nvals[] = new String[vals.length*2] ;\r
+ System.arraycopy (vars, 0, nvars, 0, vars.length) ;\r
+ System.arraycopy (vals, 0, nvals, 0, vals.length) ;\r
+ vars = nvars ;\r
+ vals = nvals ;\r
+ }\r
+ vars[varptr] = var ;\r
+ vals[varptr] = val ;\r
+ varptr++ ;\r
+ }\r
+\r
+ /**\r
+ * Debugging: print the given options of this label.\r
+ * @param out The PrintStream to print to.\r
+ */\r
+\r
+ public void print (PrintStream out) {\r
+ for (int i = 0 ; i < varptr ; i++) {\r
+ System.out.println ("\t" + vars[i] + " = " + vals[i]) ;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Does this label defines the given option ?\r
+ * @param option The option to look for.\r
+ * @return <strong>true</strong> if the label defines the given option.\r
+ */\r
+\r
+ public boolean hasOption (String option) {\r
+ for (int i = 0 ; i < varptr ; i++) {\r
+ if ( vars[i].equals (option) )\r
+ return true ;\r
+ }\r
+ return false ;\r
+ }\r
+\r
+ /**\r
+ * Get an option index.\r
+ * This allows for fast acces to label options. There is no guarantee that\r
+ * the same option will get the same index across labels.\r
+ * @param option The option to look for.\r
+ * @return An integer, which is the option index if the option was found\r
+ * or <strong>-1</strong> if the option isn't defined for this label.\r
+ */\r
+\r
+ public int getOptionIndex (String option) {\r
+ for (int i = 0 ; i < varptr ; i++) {\r
+ if ( vars[i].equals (option) )\r
+ return i;\r
+ }\r
+ return -1 ;\r
+ }\r
+\r
+ /**\r
+ * Get an option value, by index.\r
+ * This, along with the <strong>getOptionIndex</strong> method provides\r
+ * a fast access to option values.\r
+ * @param idx The index of the option, as gotten from \r
+ * <strong>getOptionIndex</strong>.\r
+ * @return A String instance, providing the option value.\r
+ */\r
+\r
+ public String getOptionValue (int idx) {\r
+ if ( (idx < 0) || (idx >= varptr) ) \r
+ throw new RuntimeException (this.getClass().getName()\r
+ + "[getOptionValue]: "\r
+ + " invalid index.") ;\r
+ return vals[idx] ;\r
+ }\r
+\r
+ /**\r
+ * Get an option value, by option name.\r
+ * @param option The option that you want the value of.\r
+ * @return A String instance giving the option value, or \r
+ * <strong>null</strong>, if the option isn't defined for this label.\r
+ */\r
+\r
+ public String getOptionValue (String option) {\r
+ for (int i = 0 ; i < varptr ; i++) {\r
+ if ( vars[i].equals (option) ) \r
+ return vals[i] ;\r
+ }\r
+ return null ;\r
+ }\r
+\r
+ /**\r
+ * Is this label generic ?\r
+ * @return <strong>true</strong> if the label if generic.\r
+ */\r
+\r
+ public boolean isGeneric () {\r
+ int idx = getOptionIndex ("generic") ;\r
+ if ( idx >= 0 )\r
+ return (new Boolean(getOptionValue (idx))).booleanValue() ;\r
+ return false ;\r
+ }\r
+\r
+ /**\r
+ * Emit the given option to the output.\r
+ * If the option is not defined for this label, skip it (don't emit). If \r
+ * possible, and according to the format, emit the short option name.\r
+ * @param space Should we emit a leading space.\r
+ * @param option The option name.\r
+ * @return <strong>true</strong> if the option was successfully emited,\r
+ * <strong>false</strong> otherwise.\r
+ */\r
+\r
+ private boolean emit (boolean space\r
+ , StringBuffer into\r
+ , String option, int format) {\r
+ String value = getOptionValue (option) ;\r
+ if ( value != null ) {\r
+ switch (format) {\r
+ case LabelBureauInterface.FMT_MINIMAL:\r
+ case LabelBureauInterface.FMT_SHORT:\r
+ // emit short option name\r
+ if ( space )\r
+ into.append (" ") ;\r
+ into.append (option+" "+value) ;\r
+ break ;\r
+ default:\r
+ // emit full option name\r
+ if ( space )\r
+ into.append (" ") ;\r
+ into.append (option+" "+value) ;\r
+ break ;\r
+ }\r
+ return true ;\r
+ }\r
+ return false ;\r
+ }\r
+\r
+ /**\r
+ * Emit the given option to the output.\r
+ * If possible, and according to the format, emit the short option name.\r
+ * @param space Should we emit a leading space.\r
+ * @param into The StringBuffer to dump this label to.\r
+ * @param option The option name.\r
+ */\r
+\r
+ private void emit (boolean space, StringBuffer into, int idx, int format) {\r
+ switch (format) {\r
+ case LabelBureauInterface.FMT_MINIMAL:\r
+ case LabelBureauInterface.FMT_SHORT:\r
+ // emit short option name\r
+ if ( space )\r
+ into.append (" ") ;\r
+ into.append (vars[idx]+" "+vals[idx]) ;\r
+ break ;\r
+ default:\r
+ if ( space )\r
+ into.append (" ") ;\r
+ into.append (vars[idx]+" "+vals[idx]) ;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Dump this label according to the given format.\r
+ * The dump should take place <em>after</em> the service has dump itself.\r
+ * @param into A StringBuffer to dump the labels to.\r
+ * @param format The format in which the dump should be done. This can\r
+ * be one of the four format described in the PICS specification.\r
+ */\r
+\r
+ public void dump (StringBuffer into, int format) {\r
+ boolean space = false ; ;\r
+ switch (format) {\r
+ case LabelBureauInterface.FMT_MINIMAL:\r
+ // emit just the ratings, except if generic\r
+ if ( isGeneric() ) {\r
+ // emit the generic and for options (FIXME errors ?)\r
+ emit (space,into,"generic",LabelBureauInterface.FMT_MINIMAL);\r
+ space = true ;\r
+ emit (space,into,"for",LabelBureauInterface.FMT_MINIMAL) ;\r
+ emit (space,into,"ratings",LabelBureauInterface.FMT_MINIMAL);\r
+ } else {\r
+ emit (space,into,"ratings",LabelBureauInterface.FMT_MINIMAL);\r
+ }\r
+ break ;\r
+ case LabelBureauInterface.FMT_SHORT:\r
+ // emit ratings, and full, which I deem appropriate\r
+ if ( isGeneric() ) {\r
+ // emit the generic and for options:\r
+ emit (space,into, "generic", LabelBureauInterface.FMT_SHORT);\r
+ space = true ;\r
+ emit (space,into, "for", LabelBureauInterface.FMT_SHORT) ;\r
+ emit (space,into, "full", LabelBureauInterface.FMT_SHORT) ;\r
+ emit (space,into, "ratings", LabelBureauInterface.FMT_SHORT);\r
+ } else {\r
+ emit (space, into, "full", LabelBureauInterface.FMT_SHORT) ;\r
+ space = true ;\r
+ emit (space,into,"ratings",LabelBureauInterface.FMT_SHORT) ;\r
+ }\r
+ break ;\r
+ case LabelBureauInterface.FMT_FULL:\r
+ // Emit all options, rating at the end\r
+ for (int i = 0 ; i < varptr ; i++) {\r
+ if ( vars[i].equals("ratings") )\r
+ continue ;\r
+ emit (space, into, i, LabelBureauInterface.FMT_FULL) ;\r
+ space = true ;\r
+ }\r
+ emit (space, into, "ratings", LabelBureauInterface.FMT_FULL) ;\r
+ break ;\r
+ case LabelBureauInterface.FMT_SIGNED:\r
+ throw new RuntimeException (this.getClass().getName()\r
+ + "[dump]: "\r
+ + "SIGNED format unsupported.") ;\r
+ \r
+ // not reached\r
+ default:\r
+ throw new RuntimeException (this.getClass().getName()\r
+ + "[dump]: "\r
+ + " invalid format "\r
+ + "\"" + format + "\"") ;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Create a new label from the given stream.\r
+ * @param file The file inwhich the label options are described.\r
+ */\r
+\r
+ SampleLabel (File file) \r
+ throws InvalidLabelException\r
+ {\r
+ try {\r
+ this.vars = new String[VARSIZE] ;\r
+ this.vals = new String[VARSIZE] ;\r
+ this.varptr = 0 ;\r
+ LabelParser p = new LabelParser (file) ;\r
+ p.parse (this) ;\r
+ } catch (InvalidLabelException e) {\r
+ throw new InvalidLabelFileException(e.getMessage());\r
+ }\r
+ }\r
+\r
+ SampleLabel (String string) \r
+ throws InvalidLabelException\r
+ {\r
+ this.vars = new String[VARSIZE] ;\r
+ this.vals = new String[VARSIZE] ;\r
+ this.varptr = 0 ;\r
+ LabelParser p = new LabelParser (string) ;\r
+ p.parse (this) ;\r
+ }\r
+ /**\r
+ * Create a new label from a set of options.\r
+ * This constructor takes two arrays, one of option names, and one\r
+ * of option values. It builds out of them the internal\r
+ * representation for this label.\r
+ * <p>The given arrays are used as is (no copy), and might side-effected\r
+ * by the new instance.\r
+ * @param optnames Names of option for this label.\r
+ * @param optvalues Values of option for this label. \r
+ */\r
+\r
+ public SampleLabel (String optnames[], String optvals[]) {\r
+ if ( optnames.length != optvals.length )\r
+ throw new RuntimeException (this.getClass().getName()\r
+ + " invalid constructor params:"\r
+ + " bad length.") ;\r
+ this.vars = optnames ;\r
+ this.vals = optvals ;\r
+ this.varptr = optnames.length ;\r
+ }\r
+\r
+ // Testing only\r
+\r
+ public static void main (String args[]) {\r
+ if ( args.length != 1 ) {\r
+ System.out.println ("Label <file>") ;\r
+ System.exit (1) ;\r
+ }\r
+ try {\r
+ SampleLabel label = new SampleLabel (new File (args[0])) ;\r
+ StringBuffer sb = new StringBuffer() ;\r
+ label.dump (sb, LabelBureauInterface.FMT_MINIMAL) ;\r
+ System.out.println (sb.toString()) ;\r
+ } catch (InvalidLabelException e) {\r
+ System.out.println (e.getMessage()) ;\r
+ System.exit (1) ;\r
+ }\r
+ System.exit (0) ;\r
+ }\r
+\r
+}\r
+\r
+\r