Bug 568296: [RJ-Data] Simplify use of raw/byte array data

  - Add RRawStore.toRawArray returning a primitive byte array
  - Add DefaultRObjectFactory.createRawVector(byte[])
  - Add functions to RDataUtils similar to the other data types

Change-Id: I4eb08a22d0c8ed5a66385a09c5707e1ad57c5c17
diff --git a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/RDataUtilsTest.java b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/RDataUtilsTest.java
index 7560b2d..cc2c2cb 100644
--- a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/RDataUtilsTest.java
+++ b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/RDataUtilsTest.java
@@ -330,4 +330,64 @@
 				RDataUtils.checkSingleCharValue(FACTORY.createCharVector(2)));
 	}
 	
+	@Test
+	public void isSingleRaw() {
+		assertTrue(RDataUtils.isSingleRaw(FACTORY.createRawVector(1)));
+		assertTrue(RDataUtils.isSingleRaw(FACTORY.createRawVector(new byte[] { 1 })));
+		
+		assertFalse(RDataUtils.isSingleRaw(null));
+		assertFalse(RDataUtils.isSingleRaw(RNullImpl.INSTANCE));
+		assertFalse(RDataUtils.isSingleRaw(FACTORY.createLogiVector(1)));
+		assertFalse(RDataUtils.isSingleRaw(FACTORY.createRawVector(0)));
+		assertFalse(RDataUtils.isSingleRaw(FACTORY.createRawVector(2)));
+	}
+	
+	@Test
+	public void checkSingleRaw() {
+		try {
+			assertEquals(Byte.valueOf((byte)0),
+					RDataUtils.checkSingleRaw(FACTORY.createRawVector(1)));
+			assertEquals(Byte.valueOf((byte)1),
+					RDataUtils.checkSingleRaw(FACTORY.createRawVector(new byte[] { 1 })));
+		}
+		catch (final UnexpectedRDataException e) {
+			fail(e);
+		}
+		
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRaw(null));
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRaw(RNullImpl.INSTANCE));
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRaw(FACTORY.createLogiVector(1)));
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRaw(FACTORY.createRawVector(0)));
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRaw(FACTORY.createRawVector(2)));
+	}
+	
+	@Test
+	public void checkSingleRawValue() {
+		try {
+			assertEquals(Byte.valueOf((byte)0),
+					RDataUtils.checkSingleRawValue(FACTORY.createRawVector(1)));
+			assertEquals(Byte.valueOf((byte)1),
+					RDataUtils.checkSingleRawValue(FACTORY.createRawVector(new byte[] { 1 })));
+		}
+		catch (final UnexpectedRDataException e) {
+			fail(e);
+		}
+		
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRawValue(null));
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRawValue(RNullImpl.INSTANCE));
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRawValue(FACTORY.createLogiVector(1)));
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRawValue(FACTORY.createRawVector(0)));
+		assertThrows(UnexpectedRDataException.class, () ->
+				RDataUtils.checkSingleRawValue(FACTORY.createRawVector(2)));
+	}
+	
 }
diff --git a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RRawStoreTest.java b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RRawStoreTest.java
index 108e72e..9e1db9f 100644
--- a/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RRawStoreTest.java
+++ b/core/org.eclipse.statet.rj.data-tests/src/org/eclipse/statet/rj/data/impl/RRawStoreTest.java
@@ -211,6 +211,20 @@
 		assertIndexOutOfBounds(data, store::getRaw, store::getRaw);
 	}
 	
+	@ParameterizedTest
+	@MethodSource("provideCaseDatas")
+	public void toRawArray(final RawCaseData data) {
+		final RRawStore store= createStore(data);
+		
+		final byte[] array= store.toRawArray();
+		
+		assertEquals(data.values.length, array.length);
+		
+		for (int i= 0; i < data.length; i++) {
+			assertEquals(data.values[i], array[i], arrayDiffersAt(i));
+		}
+	}
+	
 	
 	@ParameterizedTest
 	@MethodSource("provideCaseDatas")
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RDataUtils.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RDataUtils.java
index e8f4c02..f10e9d6 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RDataUtils.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RDataUtils.java
@@ -273,6 +273,13 @@
 		return (RCharacterStore)store;
 	}
 	
+	private static final RRawStore requireRRawStore(final RStore<?> store) throws UnexpectedRDataException {
+		if (store.getStoreType() != RStore.RAW) {
+			throw new UnexpectedRDataException("Unexpected R data type: " + getStoreAbbr(store));
+		}
+		return (RRawStore)store;
+	}
+	
 	private static final void requireLengthEqual1(final RStore<?> store) throws UnexpectedRDataException {
 		if (store.getLength() != 1) {
 			throw new UnexpectedRDataException("Unexpected R data length: " + store.getLength() + ", but == 1 expected.");
@@ -338,6 +345,18 @@
 		return (RVector<RCharacterStore>)obj;
 	}
 	
+	@SuppressWarnings("unchecked")
+	public static final RVector<RRawStore> checkRRawVector(final @Nullable RObject obj) throws UnexpectedRDataException {
+		if (obj == null) {
+			throw new UnexpectedRDataException("Missing R object.");
+		}
+		if (obj.getRObjectType() != RObject.TYPE_VECTOR) {
+			throw new UnexpectedRDataException("Unexpected R object type: " + getObjectTypeName(obj.getRObjectType()));
+		}
+		requireRRawStore(requireObjectData(obj));
+		return (RVector<RRawStore>) obj;
+	}
+	
 	
 	public static final RArray<?> checkRArray(final @Nullable RObject obj) throws UnexpectedRDataException {
 		if (obj == null) {
@@ -399,6 +418,24 @@
 		return array;
 	}
 	
+	@SuppressWarnings("unchecked")
+	public static final RArray<RRawStore> checkRRawArray(final @Nullable RObject obj, final int dim) throws UnexpectedRDataException {
+		if (obj == null) {
+			throw new UnexpectedRDataException("Missing R object.");
+		}
+		if (obj.getRObjectType() != RObject.TYPE_ARRAY) {
+			throw new UnexpectedRDataException("Unexpected R object type: " + getObjectTypeName(obj.getRObjectType()));
+		}
+		requireRRawStore(requireObjectData(obj));
+		final var array= (RArray<RRawStore>)obj;
+		if (dim > 0) {
+			if (dim != array.getDim().getLength()) {
+				throw new UnexpectedRDataException("Unexpected R array dimension: " + array.getDim().getLength());
+			}
+		}
+		return array;
+	}
+	
 	
 	public static final RList checkRList(final @Nullable RObject obj) throws UnexpectedRDataException {
 		if (obj == null) {
@@ -598,6 +635,33 @@
 		return data.getChar(0);
 	}
 	
+	public static final boolean isSingleRaw(final @Nullable RObject obj) {
+		final RStore<?> data;
+		return (obj != null && (data= obj.getData()) != null
+				&& data.getStoreType() == RStore.RAW
+				&& data.getLength() == 1 );
+	}
+	
+	public static final Byte checkSingleRaw(final @Nullable RObject obj) throws UnexpectedRDataException {
+		if (obj == null) {
+			throw new UnexpectedRDataException("Missing R object.");
+		}
+		final RRawStore data= requireRRawStore(requireObjectData(obj));
+		requireLengthEqual1(data);
+		
+		return data.get(0);
+	}
+	
+	public static final byte checkSingleRawValue(final @Nullable RObject obj) throws UnexpectedRDataException {
+		if (obj == null) {
+			throw new UnexpectedRDataException("Missing R object.");
+		}
+		final RRawStore data= requireRRawStore(requireObjectData(obj));
+		requireLengthEqual1(data);
+		
+		return data.getRaw(0);
+	}
+	
 	
 	public static final RObject checkType(final @Nullable RObject obj, final byte objectType) throws UnexpectedRDataException {
 		if (obj == null) {
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RRawStore.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RRawStore.java
index a0366a6..2441afe 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RRawStore.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/RRawStore.java
@@ -35,6 +35,8 @@
 	int MAX_INT= (MAX_BYTE & 0xFF);
 	
 	
+	byte[] toRawArray();
+	
 	@Override
 	Byte get(int idx);
 	@Override
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/DefaultRObjectFactory.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/DefaultRObjectFactory.java
index 8238b3a..2841dfd 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/DefaultRObjectFactory.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/DefaultRObjectFactory.java
@@ -217,7 +217,7 @@
 	 * the vector is initialized with "" (empty String) values.</p>
 	 * 
 	 * @param length the length of the vector
-	 * @return the R charcter vector
+	 * @return the R character vector
 	 */
 	public RVector<RCharacterStore> createCharVector(final int length) {
 		return createVector(createCharData(length), RObject.CLASSNAME_CHARACTER);
@@ -225,14 +225,29 @@
 	
 	/**
 	 * Creates an R raw vector of the specified length.
+	 * 
+	 * <p>The vector has the default R class name 'raw'.</p>
+	 * 
+	 * <p>Note that the R vector may use the array directly. For values
+	 * see {@link RStore#setRaw(int, byte)}.</p>
+	 * 
+	 * @param raws the raw/byte values of the vector
+	 * @return the R raw vector
+	 */
+	public RVector<RRawStore> createRawVector(final byte[] raws) {
+		return createVector(createRawData(raws), RObject.CLASSNAME_RAW);
+	}
+	
+	/**
+	 * Creates an R raw vector of the specified length.
 	 * <p>
 	 * The vector has the default R class name 'raw'.</p>
 	 * <p>
-	 * The function works analog to the R function <code>raw(length)</code>;
-	 * the vector is initialized with 0.0 values.</p>
+	 * The function works analog to the R function {@code raw(length)};
+	 * the vector is initialized with {@code 00} values.</p>
 	 * 
 	 * @param length the length of the vector
-	 * @return the R complex vector
+	 * @return the R raw vector
 	 */
 	public RVector<RRawStore> createRawVector(final int length) {
 		return createVector(createRawData(length), RObject.CLASSNAME_RAW);
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRaw32Store.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRaw32Store.java
index d41339d..e0bdf8a 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRaw32Store.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRaw32Store.java
@@ -18,6 +18,7 @@
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.util.Arrays;
 
 import org.eclipse.statet.jcommons.lang.NonNull;
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
@@ -142,6 +143,11 @@
 	}
 	
 	@Override
+	public byte[] toRawArray() {
+		return Arrays.copyOf(this.byteValues, this.length);
+	}
+	
+	@Override
 	public void setRaw(final int idx, final byte value) {
 		this.byteValues[idx]= value;
 	}
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRawFix64Store.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRawFix64Store.java
index 492de46..d06b255 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRawFix64Store.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRawFix64Store.java
@@ -123,6 +123,18 @@
 	}
 	
 	@Override
+	public byte[] toRawArray() {
+		final int l= checkToArrayLength();
+		final var array= new byte [l];
+		for (int i= 0, destIdx= 0; i < this.byteValues.length; i++) {
+			final byte[] raws= this.byteValues[i];
+			System.arraycopy(raws, 0, array, destIdx, raws.length);
+			destIdx+= raws.length;
+		}
+		return array;
+	}
+	
+	@Override
 	public void setRaw(final int idx, final byte value) {
 		this.byteValues[idx / SEGMENT_LENGTH][idx % SEGMENT_LENGTH] =
 				value;
diff --git a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRawStructStore.java b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRawStructStore.java
index b4fb941..7577ddd 100644
--- a/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRawStructStore.java
+++ b/core/org.eclipse.statet.rj.data/src/org/eclipse/statet/rj/data/impl/RRawStructStore.java
@@ -14,9 +14,13 @@
 
 package org.eclipse.statet.rj.data.impl;
 
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
 import org.eclipse.statet.rj.data.RStore;
 
 
+@NonNullByDefault
 public class RRawStructStore extends AbstractRawStore {
 	
 	
@@ -58,6 +62,12 @@
 	
 	
 	@Override
+	public byte[] toRawArray() {
+		throw new UnsupportedOperationException();
+	}
+	
+	
+	@Override
 	public Byte get(final int idx) {
 		throw new UnsupportedOperationException();
 	}
@@ -68,7 +78,7 @@
 	}
 	
 	@Override
-	public final Byte[] toArray() {
+	public @NonNull Byte [] toArray() {
 		throw new UnsupportedOperationException();
 	}