33 package com.google.protobuf.jruby;
39 import org.jruby.anno.JRubyClass;
40 import org.jruby.anno.JRubyMethod;
41 import org.jruby.internal.runtime.methods.DynamicMethod;
42 import org.jruby.runtime.Block;
43 import org.jruby.runtime.ObjectAllocator;
44 import org.jruby.runtime.ThreadContext;
45 import org.jruby.runtime.builtin.IRubyObject;
46 import org.jruby.util.ByteList;
48 import java.security.MessageDigest;
49 import java.security.NoSuchAlgorithmException;
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.List;
55 @JRubyClass(
name =
"Map", include =
"Enumerable")
58 RubyModule protobuf = runtime.getClassFromPath(
"Google::Protobuf");
59 RubyClass
cMap = protobuf.defineClassUnder(
"Map", runtime.getObject(),
new ObjectAllocator() {
61 public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) {
62 return new RubyMap(ruby, rubyClass);
65 cMap.includeModule(runtime.getEnumerable());
69 public RubyMap(Ruby ruby, RubyClass rubyClass) {
70 super(ruby, rubyClass);
104 @JRubyMethod(required = 2, optional = 2)
105 public IRubyObject initialize(ThreadContext context, IRubyObject[]
args) {
106 this.
table =
new HashMap<IRubyObject, IRubyObject>();
121 throw context.runtime.newArgumentError(
"Invalid key type for map.");
124 int initValueArg = 2;
125 if (needTypeclass(this.valueType) &&
args.length > 2) {
126 this.valueTypeClass =
args[2];
130 this.valueTypeClass = context.runtime.getNilClass();
135 if (
args.length > initValueArg) {
136 mergeIntoSelf(context,
args[initValueArg]);
149 @JRubyMethod(
name =
"[]=")
150 public IRubyObject indexSet(ThreadContext context, IRubyObject
key, IRubyObject
value) {
170 @JRubyMethod(
name =
"[]")
171 public IRubyObject
index(ThreadContext context, IRubyObject
key) {
173 return this.
table.get(key);
174 return context.runtime.getNil();
190 @JRubyMethod(
name =
"==")
191 public IRubyObject eq(ThreadContext context, IRubyObject _other) {
192 if (_other instanceof RubyHash)
193 return toHash(context).op_equal(context, _other);
195 if (
this == other)
return context.runtime.getTrue();
196 if (!typeCompatible(other) || this.
table.size() != other.
table.size())
197 return context.runtime.getFalse();
198 for (IRubyObject
key :
table.keySet()) {
200 return context.runtime.getFalse();
202 return context.runtime.getFalse();
204 return context.runtime.getTrue();
217 return toHash(getRuntime().getCurrentContext()).inspect();
227 public IRubyObject
hash(ThreadContext context) {
229 MessageDigest digest = MessageDigest.getInstance(
"SHA-256");
230 for (IRubyObject
key :
table.keySet()) {
231 digest.update((
byte)
key.hashCode());
232 digest.update((
byte)
table.get(
key).hashCode());
234 return context.runtime.newString(
new ByteList(digest.digest()));
235 }
catch (NoSuchAlgorithmException ignore) {
236 return context.runtime.newFixnum(System.identityHashCode(
table));
247 public IRubyObject
keys(ThreadContext context) {
248 return RubyArray.newArray(context.runtime,
table.keySet());
258 public IRubyObject
values(ThreadContext context) {
259 return RubyArray.newArray(context.runtime,
table.values());
269 public IRubyObject
clear(ThreadContext context) {
271 return context.runtime.getNil();
283 public IRubyObject
each(ThreadContext context, Block block) {
284 for (IRubyObject
key :
table.keySet()) {
287 return context.runtime.getNil();
298 public IRubyObject
delete(ThreadContext context, IRubyObject
key) {
309 @JRubyMethod(
name =
"has_key?")
310 public IRubyObject hasKey(ThreadContext context, IRubyObject
key) {
311 return this.
table.containsKey(key) ? context.runtime.getTrue() : context.runtime.getFalse();
321 public IRubyObject
length(ThreadContext context) {
322 return context.runtime.newFixnum(this.
table.size());
333 public IRubyObject
dup(ThreadContext context) {
334 RubyMap newMap = newThisType(context);
335 for (
Map.Entry<IRubyObject, IRubyObject> entry :
table.entrySet()) {
336 newMap.
table.put(entry.getKey(), entry.getValue());
341 @JRubyMethod(
name =
"to_h")
342 public RubyHash toHash(ThreadContext context) {
343 return RubyHash.newHash(context.runtime,
table, context.runtime.getNil());
347 protected IRubyObject
deepCopy(ThreadContext context) {
348 RubyMap newMap = newThisType(context);
351 for (IRubyObject
key :
table.keySet()) {
357 for (IRubyObject
key :
table.keySet()) {
365 List<DynamicMessage> list =
new ArrayList<DynamicMessage>();
366 RubyClass rubyClass = (RubyClass)
descriptor.msgclass(context);
369 for (IRubyObject
key :
table.keySet()) {
373 list.add(mapMessage.
build(context));
379 if (hashmap instanceof RubyHash) {
380 ((RubyHash) hashmap).visitAll(
new RubyHash.Visitor() {
382 public void visit(IRubyObject
key, IRubyObject
val) {
383 indexSet(context,
key,
val);
386 }
else if (hashmap instanceof
RubyMap) {
388 if (!typeCompatible(other)) {
389 throw context.runtime.newTypeError(
"Attempt to merge Map with mismatching types");
392 throw context.runtime.newTypeError(
"Unknown type merging into Map");
398 return this.keyType == other.
keyType &&
405 if (needTypeclass(valueType)) {
406 newMap = (
RubyMap) metaClass.newInstance(context,
409 valueTypeClass, Block.NULL_BLOCK);
411 newMap = (
RubyMap) metaClass.newInstance(context,
416 newMap.
table =
new HashMap<IRubyObject, IRubyObject>();
433 private Map<IRubyObject, IRubyObject>
table;