1// BEGIN LICENSE BLOCK 2// Version: CMPL 1.1 3// 4// The contents of this file are subject to the Cisco-style Mozilla Public 5// License Version 1.1 (the "License"); you may not use this file except 6// in compliance with the License. You may obtain a copy of the License 7// at www.eclipse-clp.org/license. 8// 9// Software distributed under the License is distributed on an "AS IS" 10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11// the License for the specific language governing rights and limitations 12// under the License. 13// 14// The Original Code is The ECLiPSe Constraint Logic Programming System. 15// The Initial Developer of the Original Code is Cisco Systems, Inc. 16// Portions created by the Initial Developer are 17// Copyright (C) 2000 - 2006 Cisco Systems, Inc. All Rights Reserved. 18// 19// Contributor(s): Stefano Novello / Josh Singer, Parc Technologies 20// 21// END LICENSE BLOCK 22 23//Title: Java/ECLiPSe interface 24//Version: $Id: EXDRInputStream.java,v 1.1 2006/09/23 01:54:10 snovello Exp $ 25//Author: Stefano Novello / Josh Singer 26//Company: Parc Technologies 27//Description: InputStream which can parse ECLiPSe EXDR format. 28 29package com.parctechnologies.eclipse; 30import java.util.*; 31import java.io.*; 32 33 34 35/** 36 * A stream which can read EXDR format. 37 * 38 * An <i>EXDRInputStream</i> can be constructed from any 39 * instance of the <i>InputStream</i> class and extends it to be able to interpret 40 * incoming data which is in the EXDR (ECLiPSe eXternal Data Representation) format. 41 * <p> 42 * Use the method {@link EXDRInputStream#readTerm()} to convert from EXDR into 43 * Java <i>CompoundTerm</i> objects and instances of other relevant Java classes 44 * which represent ECLiPSe types. 45 * <p> Note that EXDRInputStream objects are often constructed using the 46 * {@link FromEclipseQueue} class. 47 * @see CompoundTerm 48 * @see FromEclipseQueue 49 * 50 */ 51public class EXDRInputStream extends DataInputStream 52{ 53 // note that throughout this class we use the methods of DataInputStream 54 // rather than those of the input stream in. This is because we want a 55 // blocking read (which blocks until it finishes, or raises an exception) 56 // The consequence of this is that this class' methods fulfill the same 57 // contract: they block until they are done, or they throw an exception. 58 // This is as it should be because this is a subclass of DataInputStream. 59 60 /** 61 * The following array list is used to map EXDR string references to their 62 * string value. For strings that occur repeatedly throughout the message, 63 * there is a single EXDR string representation. The remainder of the 64 * occurrences are all references to the first occurrence of the string. 65 * The array list is not synchronized. Multiple threads writing concurrently 66 * to the output stream is envisaged via the synchronized 'readTerm()'. 67 * Should this change, the array should become a Vector or wrapped: 68 * List list = Collections.synchronizedList(new ArrayList(...)); 69 * An array list is used because string reference idenitifers start from 0, 70 * as a result there is not need for a map. 71 * Duplicate strings are allowed (EXDR version 2 permits reading but *not* 72 * writing of duplicates). 73 */ 74 private ArrayList stringList = null; 75 76 /** 77 * Construct an <i>EXDRInputStream</i> using a given InputStream for 78 * incoming data. 79 */ 80 public EXDRInputStream(InputStream in) 81 { 82 super(in); 83 } 84 85 /** 86 * Read a chunk (one term's worth) of EXDR from the incoming data and 87 * convert it into the corresponding object (an instance of CompoundTerm, Integer, 88 * etc.). 89 * This works as an atomic 90 * action so no other thread can read until the complete 91 * term has been read in. 92 */ 93 synchronized public Object readTerm() throws IOException 94 { 95 // Read the first two bytes. These give the EXDR version number 96 97 int c1 = readByte(); 98 int c2 = readByte(); 99 if (c1 == 'V' && c2 < 3) 100 { 101 // Dispose of the hash map if it's hanging around 102 // (exception occurred etc) 103 stringList = null; 104 Object term = readSubTerm(); 105 // Dispose of the hash map if created 106 stringList = null; 107 108 return term; 109 } 110 else // Throw an exception if the version bytes were incorrect 111 { 112 throw new IOException("EXDR protocol error: bad version " + c1 + " " + c2); 113 } 114 } 115 116 /** 117 * Read a term's worth of data and return a Java object of the corresponding 118 * type. 119 */ 120 private Object readSubTerm() throws IOException 121 { 122 // Read the first byte, which is a capital letter that varies according 123 // to the class of data. 124 int c = readByte(); 125 switch (c) 126 { 127 case 'C' : // Turn string compression on 128 stringList = new ArrayList(); 129 return readSubTerm(); 130 case 'B' : // for byte use method inherited from DataInputStream 131 return new Integer(readByte()); // Implicit widen 132 case 'I' : // for integers use method inherited from DataInputStream 133 return new Integer(readInt()); 134 case 'J' : // for long use method inherited from DataInputStream 135 return new Long(readLong()); 136 case 'D' : // for double use method inherited from DataInputStream 137 return new Double(readDouble()); 138 case '_' : // for variables, return null (corresponding java representation) 139 return null; 140 case 'R' : // Retrieve the string from the string list 141 if (stringList == null) { 142 throw new IOException( 143 "EXDR protocol error: String compression disabled, but reference found"); 144 } 145 return stringList.get(readNat()); 146 case 'S' : // use own method for strings 147 return readString(); 148 case 'F' : // for compound terms 149 // read the arity as an integer 150 int arity = readNat(); 151 // if arity is zero 152 if(arity == 0) 153 { 154 // read the next term (it will be a string), and 155 // construct and return an atom based on it. 156 return(new Atom((String) readSubTerm())); 157 } 158 // construct an array with space for the functor and subterms 159 Object res[] = new Object[arity+1]; 160 // read in the functor and subterms and assign the array 161 // elements to the corresponding resulting Objects 162 for (int i = 0 ; i < arity+1 ; i++) 163 res[i] = readSubTerm(); 164 // Return a CompoundTermImpl constructed from this array 165 return new CompoundTermImpl(res); 166 case '[' : // for NON-empty lists 167 // create an empty list 168 List list = new LinkedList(); 169 // read and add the first element (there must be one) 170 list.add(readSubTerm()); 171 while(true) // keep reading and adding elements until we reach a ']' 172 { 173 c = readByte(); 174 if (c == ']') return list; 175 else if (c == '[') list.add(readSubTerm()); 176 else 177 throw new IOException("EXDR protocol error: unexpected list separator "+c); 178 } 179 case ']' : // for empty lists 180 return Collections.EMPTY_LIST; 181 default: // if no codes match, throw an exception 182 throw new IOException("EXDR protocol error: unrecognized EXDR code = "+c); 183 } 184 } 185 186 /** 187 * Read an 'XDR_nat' 188 */ 189 private int readNat() throws IOException 190 { 191 // Read the first byte 192 byte a = readByte(); 193 194 // If signed bit is one, the remaining 7 bits 195 // represent the value. 196 if ((a & 0x80) != 0) { 197 return (a & 0x7F); 198 } 199 200 // If signed bit is 0 it's really a 201 // four byte int, so read and construct the value 202 203 return (((a & 0xff) << 24) | ((readByte() & 0xff) << 16) | 204 ((readByte() & 0xff) << 8) | (readByte() & 0xff)); 205 } 206 207 /** 208 * Read a string 209 */ 210 private String readString() throws IOException 211 { 212 // create a byte buffer with length for the string 213 byte buff[] = new byte[readNat()]; 214 215 // read in buff.length bytes into buff 216 readFully(buff); 217 218 // construct an intern'd String representation 219 // of 'buff'. By interning we save space for large terms 220 // with repeated strings and functors 221 String string = (new String(buff)).intern(); 222 223 // Store the string in the array list for subsequent 224 // EXDR string reference resolution 225 if (stringList != null) stringList.add(string); 226 227 return string; 228 } 229} 230