KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > runtime > marshal > UnmarshalStream


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
15  * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16  * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
17  * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
18  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
19  * Copyright (C) 2006 Ola Bini <ola.bini@ki.se>
20  *
21  * Alternatively, the contents of this file may be used under the terms of
22  * either of the GNU General Public License Version 2 or later (the "GPL"),
23  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24  * in which case the provisions of the GPL or the LGPL are applicable instead
25  * of those above. If you wish to allow use of your version of this file only
26  * under the terms of either the GPL or the LGPL, and not to allow others to
27  * use your version of this file under the terms of the CPL, indicate your
28  * decision by deleting the provisions above and replace them with the notice
29  * and other provisions required by the GPL or the LGPL. If you do not delete
30  * the provisions above, a recipient may use your version of this file under
31  * the terms of any one of the CPL, the GPL or the LGPL.
32  ***** END LICENSE BLOCK *****/

33 package org.jruby.runtime.marshal;
34
35 import java.io.BufferedInputStream JavaDoc;
36 import java.io.IOException JavaDoc;
37 import java.io.InputStream JavaDoc;
38 import org.jruby.Ruby;
39 import org.jruby.RubyArray;
40 import org.jruby.RubyBignum;
41 import org.jruby.RubyClass;
42 import org.jruby.RubyFixnum;
43 import org.jruby.RubyFloat;
44 import org.jruby.RubyHash;
45 import org.jruby.RubyModule;
46 import org.jruby.RubyRegexp;
47 import org.jruby.RubyString;
48 import org.jruby.RubyStruct;
49 import org.jruby.RubySymbol;
50 import org.jruby.exceptions.RaiseException;
51 import org.jruby.runtime.Block;
52 import org.jruby.runtime.builtin.IRubyObject;
53 import org.jruby.util.ByteList;
54
55 /**
56  * Unmarshals objects from strings or streams in Ruby's marsal format.
57  *
58  * @author Anders
59  */

60 public class UnmarshalStream extends BufferedInputStream JavaDoc {
61     protected final Ruby runtime;
62     private UnmarshalCache cache;
63     private IRubyObject proc;
64
65     public UnmarshalStream(Ruby runtime, InputStream JavaDoc in, IRubyObject proc) throws IOException JavaDoc {
66         super(in);
67         this.runtime = runtime;
68         this.cache = new UnmarshalCache(runtime);
69         this.proc = proc;
70
71         in.read(); // Major
72
in.read(); // Minor
73
}
74
75     public IRubyObject unmarshalObject() throws IOException JavaDoc {
76         try {
77             int type = readUnsignedByte();
78             IRubyObject result;
79             if (cache.isLinkType(type)) {
80                 result = cache.readLink(this, type);
81             } else {
82                 result = unmarshalObjectDirectly(type);
83             }
84             return result;
85         } catch (IOException JavaDoc ioe) {
86             throw ioe;
87         }
88     }
89
90     public void registerLinkTarget(IRubyObject newObject) {
91         cache.register(newObject);
92     }
93
94     private IRubyObject unmarshalObjectDirectly(int type) throws IOException JavaDoc {
95         IRubyObject rubyObj = null;
96         switch (type) {
97             case 'I':
98                 rubyObj = unmarshalObject();
99                 defaultInstanceVarsUnmarshal(rubyObj);
100                 break;
101             case '0' :
102                 rubyObj = runtime.getNil();
103                 break;
104             case 'T' :
105                 rubyObj = runtime.getTrue();
106                 break;
107             case 'F' :
108                 rubyObj = runtime.getFalse();
109                 break;
110             case '"' :
111                 rubyObj = RubyString.unmarshalFrom(this);
112                 break;
113             case 'i' :
114                 rubyObj = RubyFixnum.unmarshalFrom(this);
115                 break;
116             case 'f' :
117                 rubyObj = RubyFloat.unmarshalFrom(this);
118                 break;
119             case '/' :
120                 rubyObj = RubyRegexp.unmarshalFrom(this);
121                 break;
122             case ':' :
123                 rubyObj = RubySymbol.unmarshalFrom(this);
124                 break;
125             case '[' :
126                 rubyObj = RubyArray.unmarshalFrom(this);
127                 break;
128             case '{' :
129                 rubyObj = RubyHash.unmarshalFrom(this, false);
130                 break;
131             case '}' :
132                 // "hashdef" object, a hash with a default
133
rubyObj = RubyHash.unmarshalFrom(this, true);
134                 break;
135             case 'c' :
136                 rubyObj = RubyClass.unmarshalFrom(this);
137                 break;
138             case 'm' :
139                 rubyObj = RubyModule.unmarshalFrom(this);
140                 break;
141             case 'l' :
142                 rubyObj = RubyBignum.unmarshalFrom(this);
143                 break;
144             case 'S' :
145                 rubyObj = RubyStruct.unmarshalFrom(this);
146                 break;
147             case 'o' :
148                 rubyObj = defaultObjectUnmarshal();
149                 break;
150             case 'u' :
151                 rubyObj = userUnmarshal();
152                 break;
153             case 'U' :
154                 rubyObj = userNewUnmarshal();
155                 break;
156             case 'C' :
157                 rubyObj = uclassUnmarshall();
158                 break;
159             default :
160                 throw getRuntime().newArgumentError("dump format error(" + (char)type + ")");
161         }
162         
163         if (proc != null && type != ':') {
164             // call the proc, but not for symbols
165
proc.callMethod(getRuntime().getCurrentContext(), "call", new IRubyObject[] {rubyObj});
166         }
167         return rubyObj;
168     }
169
170
171     public Ruby getRuntime() {
172         return runtime;
173     }
174
175     public int readUnsignedByte() throws IOException JavaDoc {
176         int result = read();
177         if (result == -1) {
178             throw new IOException JavaDoc("Unexpected end of stream");
179         }
180         return result;
181     }
182
183     public byte readSignedByte() throws IOException JavaDoc {
184         int b = readUnsignedByte();
185         if (b > 127) {
186             return (byte) (b - 256);
187         }
188         return (byte) b;
189     }
190
191     public ByteList unmarshalString() throws IOException JavaDoc {
192         int length = unmarshalInt();
193         byte[] buffer = new byte[length];
194         
195         // FIXME: sooper inefficient, but it's working better...
196
int b = 0;
197         int i = 0;
198         while (i < length && (b = read()) != -1) {
199             buffer[i++] = (byte)b;
200         }
201         if (i < length) {
202             throw new IOException JavaDoc("Unexpected end of stream");
203         }
204         return new ByteList(buffer,false);
205     }
206
207     public int unmarshalInt() throws IOException JavaDoc {
208         int c = readSignedByte();
209         if (c == 0) {
210             return 0;
211         } else if (5 < c && c < 128) {
212             return c - 5;
213         } else if (-129 < c && c < -5) {
214             return c + 5;
215         }
216         long result;
217         if (c > 0) {
218             result = 0;
219             for (int i = 0; i < c; i++) {
220                 result |= (long) readUnsignedByte() << (8 * i);
221             }
222         } else {
223             c = -c;
224             result = -1;
225             for (int i = 0; i < c; i++) {
226                 result &= ~((long) 0xff << (8 * i));
227                 result |= (long) readUnsignedByte() << (8 * i);
228             }
229         }
230         return (int) result;
231     }
232
233     private IRubyObject defaultObjectUnmarshal() throws IOException JavaDoc {
234         RubySymbol className = (RubySymbol) unmarshalObject();
235
236         RubyClass type = null;
237         try {
238             type = (RubyClass)runtime.getClassFromPath(className.asSymbol());
239         } catch (RaiseException e) {
240             if (e.getException().isKindOf(runtime.getModule("NameError"))) {
241                 throw runtime.newArgumentError("undefined class/module " + className.asSymbol());
242             }
243                 
244             throw e;
245         }
246
247         assert type != null : "type shouldn't be null.";
248
249         IRubyObject result = (IRubyObject)type.unmarshal(this);
250
251         return result;
252     }
253     
254     public void defaultInstanceVarsUnmarshal(IRubyObject object) throws IOException JavaDoc {
255         int count = unmarshalInt();
256         
257         for (int i = 0; i < count; i++) {
258             String JavaDoc name = unmarshalObject().asSymbol();
259             IRubyObject value = unmarshalObject();
260             object.setInstanceVariable(name, value);
261         }
262     }
263     
264     private IRubyObject uclassUnmarshall() throws IOException JavaDoc {
265         RubySymbol className = (RubySymbol)unmarshalObject();
266         
267         RubyClass type = (RubyClass)runtime.getClassFromPath(className.asSymbol());
268         
269         IRubyObject result = unmarshalObject();
270         
271         result.setMetaClass(type);
272         
273         return result;
274     }
275
276     private IRubyObject userUnmarshal() throws IOException JavaDoc {
277         String JavaDoc className = unmarshalObject().asSymbol();
278         ByteList marshaled = unmarshalString();
279         RubyModule classInstance;
280         try {
281             classInstance = runtime.getClassFromPath(className);
282         } catch (RaiseException e) {
283             if (e.getException().isKindOf(runtime.getModule("NameError"))) {
284                 throw runtime.newArgumentError("undefined class/module " + className);
285             }
286                 
287             throw e;
288         }
289         if (!classInstance.respondsTo("_load")) {
290             throw runtime.newTypeError("class " + classInstance.getName() + " needs to have method `_load'");
291         }
292         IRubyObject result = classInstance.callMethod(getRuntime().getCurrentContext(),
293             "_load", RubyString.newString(getRuntime(), marshaled));
294         registerLinkTarget(result);
295         return result;
296     }
297
298     private IRubyObject userNewUnmarshal() throws IOException JavaDoc {
299         String JavaDoc className = unmarshalObject().asSymbol();
300         IRubyObject marshaled = unmarshalObject();
301         RubyClass classInstance = runtime.getClass(className);
302         IRubyObject result = classInstance.newInstance(new IRubyObject[0], Block.NULL_BLOCK);
303         result.callMethod(getRuntime().getCurrentContext(),"marshal_load", marshaled);
304         registerLinkTarget(result);
305         return result;
306     }
307 }
308
Popular Tags