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