blob: 0554a080294e89d98b67f67923276a5fec203654 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pig.scripting.jruby;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import org.apache.pig.backend.executionengine.ExecException;
import org.apache.pig.data.Tuple;
import org.apache.pig.data.DataByteArray;
import org.jruby.Ruby;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyEnumerator;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.RubySymbol;
import org.jruby.RubyString;
import org.jruby.RubyFixnum;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.javasupport.JavaUtil;
import org.jruby.javasupport.JavaObject;
/**
* This class presents a native Ruby object for interacting with and manipulating
* DataByteArray objects. For more information on what the annotations mean, see
* {@link RubyDataBag}.
*/
@JRubyClass(name="DataByteArray")
public class RubyDataByteArray extends RubyObject {
private static final long serialVersionUID = 1L;
private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyDataByteArray(runtime, klass);
}
};
/**
* Registers the DataByteArray class with the Ruby runtime.
*
* @param runtime an instance of the Ruby runtime
* @return a RubyClass object with metadata about the registered class
*/
public static RubyClass define(Ruby runtime) {
RubyClass result = runtime.defineClass("DataByteArray", runtime.getObject(), ALLOCATOR);
result.kindOf = new RubyModule.KindOf() {
public boolean isKindOf(IRubyObject obj, RubyModule type) {
return obj instanceof RubyDataByteArray;
}
};
result.defineAnnotatedMethods(RubyDataByteArray.class);
return result;
}
private DataByteArray internalDBA;
/**
* This constructor encapsulated a null DataByteArray.
*
* @param ruby an instance of the ruby runtime
* @param rc an instance of the class object with meatadata
*/
protected RubyDataByteArray(final Ruby ruby, RubyClass rc) {
super(ruby,rc);
internalDBA = new DataByteArray();
}
/**
* Given a DataByteArray, this constructor creates a new one which copies the underlying bytes.
*
* @param ruby an instance of the ruby runtime
* @param rc an instance of the class object with meatadata
* @param dba a DataByteArray to copy then encapsulate
*/
protected RubyDataByteArray(final Ruby ruby, RubyClass rc, DataByteArray dba) {
this(ruby, rc);
byte[] buf1 = dba.get();
byte[] buf2 = new byte[buf1.length];
System.arraycopy(buf1, 0, buf2, 0, buf1.length);
internalDBA = new DataByteArray(buf2);
}
/**
* This constructor makes a RubyDataByteArray whose underlying bytes are a concatenation
* of the given bytes. The new DataByteArray will not point to the original bytes.
*
* @param ruby an instance of the ruby runtime
* @param rc an instance of the class object with meatadata
* @param dba1 first DataByteArray in the concatentation
* @param dba2 second DAtaByteArray whose bytes will be concatenated to the first's
*/
protected RubyDataByteArray(final Ruby ruby, RubyClass rc, DataByteArray dba1, DataByteArray dba2) {
super(ruby, rc);
internalDBA = new DataByteArray(dba1, dba2);
}
/**
* This constructor creates a new DataByteArray with a reference to the provided bytes.
*
* @param ruby an instance of the ruby runtime
* @param rc an instance of the class object with meatadata
* @param buf a byte array to encapsulate directly
*/
protected RubyDataByteArray(final Ruby ruby, RubyClass rc, byte[] buf) {
super(ruby,rc);
internalDBA = new DataByteArray(buf);
}
public DataByteArray getDBA() {
return internalDBA;
}
/**
* This is the default initializer, which returns an empty DataByteArray.
*
* @return the initialized RubyDataByteArray
*/
@JRubyMethod
@SuppressWarnings("deprecation")
public RubyDataByteArray initialize() {
internalDBA = new DataByteArray();
return this;
}
/**
* Given a String or a set of bytes[], initializes the encapsulated DataByteArray
* using {@link DataByteArray#set}. In the case of a DataByteArray, will copy
* the underlying bytes.
*
* @param arg a value to set the encapsulated DataByteArray to. A DataByteArray
will be copied, whereas a byte array will be encapsulated directly,
and a string's bits will be used per {@link DataByteArray#set}.
* @return the initialized RubyDataByteArray
*/
@JRubyMethod
public RubyDataByteArray initialize(IRubyObject arg) {
if (arg instanceof RubyString) {
internalDBA.set(arg.toString());
} else if (arg instanceof RubyDataByteArray) {
byte[] buf1 = ((RubyDataByteArray)arg).getDBA().get();
byte[] buf2 = new byte[buf1.length];
System.arraycopy(buf1, 0, buf2, 0, buf1.length);
internalDBA = new DataByteArray(buf2);
} else {
internalDBA.set((byte[])arg.toJava(byte[].class));
}
return this;
}
/**
* Given two RubyDataByteArrays, initializes the encapsulated DataByteArray
* to be a concatentation of the copied bits of the first and second.
*
* @param arg1 the first RubyDataByteArray whose bits will be used
* @param arg2 the second RubyDataByteArray whose bits will be concatenated
after the first's
* @return the initialized RubyDataBytearray
*/
@JRubyMethod
public RubyDataByteArray initialize(IRubyObject arg1, IRubyObject arg2) {
if (arg1 instanceof RubyDataByteArray && arg2 instanceof RubyDataByteArray) {
internalDBA = new DataByteArray(((RubyDataByteArray)arg1).getDBA(), ((RubyDataByteArray)arg2).getDBA());
} else {
throw new IllegalArgumentException("Invalid arguments passed to DataByteArray");
}
return this;
}
/**
* This calls to the append function of the underlying DataByteArray. This accepts
* the same types that set accepts.
*
* @param context the context the method is being executed in
* @param arg a value to append to the encpasulated DataByteArray. In the case of a
* RubyDataByteArray, the bytes will be copied and appended; in the case
* of a String, the bits will be added; in the case of a byte array,
* the bytes will be appended directly.
*/
@JRubyMethod(name = {"add!", "append"})
public void append(ThreadContext context, IRubyObject arg) {
Ruby runtime = context.getRuntime();
RubyDataByteArray toAppend = new RubyDataByteArray(runtime, runtime.getClass("DataByteArray")).initialize(arg);
internalDBA.append(toAppend.getDBA());
}
/**
* This calls to the static method compare of DataByteArray. Given two DataByteArrays, it will call it
* on the underlying bytes.
*
* @param context the context the method is being executed in
* @param self an class which contains metadata on the calling class (required for static Ruby methods)
* @param arg1 a RubyDataByteArray or byte array to compare
* @param arg2 a RubyDataByteArray or byte array to compare
* @return the Fixnum result of comparing arg1 and arg2's bytes
*/
@JRubyMethod
public static RubyFixnum compare(ThreadContext context, IRubyObject self, IRubyObject arg1, IRubyObject arg2) {
byte[] buf1, buf2;
if (arg1 instanceof RubyDataByteArray) {
buf1 = ((RubyDataByteArray)arg1).getDBA().get();
} else {
buf1 = (byte[])arg1.toJava(byte[].class);
}
if (arg2 instanceof RubyDataByteArray) {
buf2 = ((RubyDataByteArray)arg2).getDBA().get();
} else {
buf2 = (byte[])arg2.toJava(byte[].class);
}
return RubyFixnum.newFixnum(context.getRuntime(), DataByteArray.compare(buf1, buf2));
}
/**
* This calls the compareTo method of the encapsulated DataByteArray.
*
* @param context the context the method is being executed in
* @param arg a RubyDataByteArray or byte array to compare to the
* encapsulated DataByteArray
* @return the result of compareTo on the encapsulated DataByteArray
and arg
*/
@JRubyMethod
public RubyFixnum compareTo(ThreadContext context, IRubyObject arg) {
int compResult;
if (arg instanceof RubyDataByteArray) {
compResult = internalDBA.compareTo(((RubyDataByteArray)arg).getDBA());
} else {
compResult = internalDBA.compareTo(new DataByteArray((byte[])arg.toJava(byte[].class)));
}
return RubyFixnum.newFixnum(context.getRuntime(), compResult);
}
/**
* Overrides equality leveraging DataByteArray's equality.
*
* @param context the context the method is being executed in
* @param arg a RubyDataByteArray against which to test equality
* @return true if they are equal, false otherwise
*/
@JRubyMethod(name = {"eql?", "=="})
public RubyBoolean equals(ThreadContext context, IRubyObject arg) {
Ruby runtime = context.getRuntime();
if (arg instanceof RubyDataByteArray) {
return RubyBoolean.newBoolean(runtime, internalDBA.equals(((RubyDataByteArray)arg).getDBA()));
} else {
return runtime.getFalse();
}
}
/**
* Overrides ruby's hash leveraging DataByteArray's hash.
*
* @param context the context the method is being executed in
* @return the encapsulated DataByteArray's hashCode()
*/
@JRubyMethod
public IRubyObject hash(ThreadContext context) {
return RubyFixnum.newFixnum(context.getRuntime(), internalDBA.hashCode());
}
/**
* This method calls the set method of the underlying DataByteArray with one exception:
* if given a RubyDataByteArray, it will set the encapsulated DataByteArray to be equal.
*
* @param arg an object to set the encapsulated DataByteArray's bits to. In the case of
* a RubyString or byte array, the underlying bytes will be sit directly. In
* the case of a RubyDataByteArray, the encapsulated DataByteArray will be set
* equal to arg.
*/
@JRubyMethod
public void set(IRubyObject arg) {
if (arg instanceof RubyDataByteArray) {
internalDBA = ((RubyDataByteArray)arg).getDBA();
} else if (arg instanceof RubyString) {
internalDBA.set(arg.toString());
} else {
internalDBA.set((byte[])arg.toJava(byte[].class));
}
}
/**
* @param context the context the method is being executed in
* @return the size of the encapsulated DataByteArray
*/
@JRubyMethod(name = {"size", "length"})
public RubyFixnum size(ThreadContext context) {
return RubyFixnum.newFixnum(context.getRuntime(), internalDBA.size());
}
/**
* This method accepts as an argument anything that could be passed to set (ie a
* RubyDataByteArray, RubyString, or byte array). It then adds those values together.
*
* @param context the context the method is being executed in
* @param arg any argument that can validly initialize a RubyDataByteArray
* @return a <u>new</u> RubyDataByteArray that is the concatentation of
* the encapsulated DataByteArray and arg
*/
@JRubyMethod(name = "+")
public IRubyObject plus(ThreadContext context, IRubyObject arg) {
Ruby runtime = context.getRuntime();
RubyDataByteArray toAdd = new RubyDataByteArray(runtime, runtime.getClass("DataByteArray")).initialize(arg);
return new RubyDataByteArray(runtime, runtime.getClass("DataByteArray"), internalDBA, toAdd.getDBA());
}
/**
* @param context the context the method is being executed in
* @return the string representation of the encapsulated DataByteArray
*/
@JRubyMethod(name = {"to_s", "inspect"})
public IRubyObject toString(ThreadContext context) {
return RubyString.newString(context.getRuntime(), internalDBA.toString());
}
}