blob: 3d59ab5608424ccdc49d94934352152cf24fc2f7 [file] [log] [blame]
<%@ jet package="org.eclipse.emf.henshin.giraph.templates" class="HenshinUtilTemplate" imports="java.util.*"%>
<%
@SuppressWarnings("unchecked")
Map<String,Object> args = (Map<String,Object>) argument;
String packageName = (String) args.get("packageName");
//boolean logging = (Boolean) args.get("logging");
%>
package <%= packageName %>;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import org.apache.giraph.aggregators.BasicAggregator;
import org.apache.giraph.edge.Edge;
import org.apache.giraph.edge.EdgeFactory;
import org.apache.giraph.graph.Vertex;
import org.apache.giraph.io.formats.TextVertexInputFormat;
import org.apache.giraph.io.formats.TextVertexOutputFormat;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.ByteWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.json.JSONArray;
import org.json.JSONException;
/**
* Henshin utility classes and methods.
*/
public class HenshinUtil {
/**
* Length of integers in bytes.
*/
private static final int INT_LENGTH = Integer.SIZE / Byte.SIZE;
/**
* Private constructor.
*/
private HenshinUtil() {
// Prevent instantiation
}
/**
* Remove duplicate matches.
* @param matches List of matches.
* @return Filtered list.
*/
public static List<Match> removeDuplicateMatches(Iterable<Match> matches) {
Set<Match> result = new LinkedHashSet<>();
for (Match m : matches) {
result.add(m);
}
return new ArrayList<>(result);
}
/**
* Remove non-injective matches.
* @param matches List of matches.
* @return Filtered list.
*/
public static List<Match> removeNonInjectiveMatches(Iterable<Match> matches) {
List<Match> result = new ArrayList<>();
for (Match m : matches) {
if (m.isInjective()) {
result.add(m);
}
}
return result;
}
/**
* Henshin data.
*/
public abstract static class Bytes extends BytesWritable {
/**
* Default constructor.
*/
public Bytes() {
super();
}
/**
* Extra constructor.
* @param data The data.
*/
public Bytes(byte[] data) {
super(data);
}
/**
* Set the size.
* @param size The new size.
*/
@Override
public void setSize(int size) {
if (size != getCapacity()) {
setCapacity(size);
}
super.setSize(size);
}
/**
* Pretty-print this bytes object.
* @return The printed string.
*/
@Override
public String toString() {
byte[] bytes = getBytes();
StringBuffer result = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
result.append(bytes[i]);
if (i < bytes.length - 1) {
result.append(",");
}
}
return "[" + result + "]";
}
}
/**
* Henshin vertex ID.
*/
public static class VertexId extends Bytes {
/**
* Default constructor.
*/
public VertexId() {
super();
}
/**
* Extra constructor.
* @param data The data.
*/
public VertexId(byte[] data) {
super(data);
}
/**
* Create a new random vertex ID.
* The vertex ID is derived from a random UUID.
* @return The new vertex ID.
*/
public static VertexId randomVertexId() {
UUID uuid = UUID.randomUUID();
byte[] bytes = new byte[(Long.SIZE / Byte.SIZE) * 2];
ByteBuffer buffer = ByteBuffer.wrap(bytes);
LongBuffer longBuffer = buffer.asLongBuffer();
longBuffer.put(new long[] {
uuid.getMostSignificantBits(),
uuid.getLeastSignificantBits()
});
return new VertexId(bytes);
}
/**
* Create an extended version of this vertex ID.
* @param value The value to be appended to this vertex ID.
* @return The extended version of this vertex ID.
*/
public VertexId append(byte value) {
byte[] bytes = getBytes();
bytes = Arrays.copyOf(bytes, bytes.length + 1);
bytes[bytes.length - 1] = value;
return new VertexId(bytes);
}
}
/**
* Henshin match object.
*/
public static class Match extends Bytes {
/**
* Number of bytes before match starts
*/
private final int PREAMBLE = 5;
/**
* Microstep position
*/
private final int MICROSTEP_POS = 4;
/**
* Empty match.
*/
public static final Match EMPTY = new Match();
/**
* Default constructor.
*/
public Match() {
super();
}
/**
* Extra constructor.
* @param segment The segment of this match.
*/
public Match(int segment, int microstep) {
super(new byte[] {
(byte) (segment >>> 24),
(byte) (segment >>> 16),
(byte) (segment >>> 8),
(byte) segment,
(byte) microstep });
}
/**
* Extra constructor.
* @param data The data.
*/
public Match(byte[] data) {
super(data);
}
public byte getMicrostep(){
return getBytes()[MICROSTEP_POS];
}
public Match setMicrostep(byte microstep){
return setByte(microstep, MICROSTEP_POS);
}
private Match setByte(byte data, int position){
byte[] bytes = getBytes();
byte[] result = Arrays.copyOf(bytes, bytes.length);
result[position] = data;
return new Match(result);
}
/**
* Get the segment of this match.
* @return The segment.
*/
public int getSegment() {
byte[] bytes = getBytes();
return bytes[0] << 24 |
(bytes[1] & 0xFF) << 16 |
(bytes[2] & 0xFF) << 8 |
(bytes[3] & 0xFF);
}
/**
* Get the size of this match.
* @return The match size.
*/
public int getMatchSize() {
byte[] bytes = getBytes();
int d = PREAMBLE;
int size = 0;
while (d < bytes.length) {
d += bytes[d] + 1;
size++;
}
return size;
}
/**
* Get the vertex ID of a matched node.
* @param vertexIndex Index of the next vertex.
* @return The vertex ID.
*/
public VertexId getVertexId(int vertexIndex) {
byte[] bytes = getBytes();
int d = PREAMBLE;
for (int i = 0; i < vertexIndex; i++) {
if (d >= bytes.length) {
return null;
}
d += bytes[d] + 1;
}
if (d >= bytes.length) {
return null;
}
return new VertexId(
Arrays.copyOfRange(bytes, d + 1, d + 1 + bytes[d]));
}
/**
* Get the index of a vertex ID of a matched node.
* @param vertexId A vertex ID.
* @return The index of the vertex ID or -1.
*/
public int indexOf(VertexId vertexId) {
int i = 0;
VertexId id;
do {
id = getVertexId(i);
if (vertexId.equals(id)) {
return i;
}
i++;
} while (id != null);
return -1;
}
/**
* Returns true if this match is injective.
* @return true if this match is injective.
*/
public boolean isInjective() {
Set<VertexId> ids = new HashSet<>();
int i = 0;
VertexId id;
do {
id = getVertexId(i++);
if (id != null && !ids.add(id)) {
return false;
}
} while (id != null);
return true;
}
/**
* Create an extended version of this (partial) match.
* @param vertexId The ID of the next matched vertex.
* @return The extended match object.
*/
public Match append(VertexId vertexId) {
byte[] bytes = getBytes();
byte[] id = vertexId.getBytes();
byte[] result = Arrays.copyOf(bytes, bytes.length + 1 + id.length);
result[bytes.length] = (byte) id.length;
System.arraycopy(id, 0, result, bytes.length + 1, id.length);
return new Match(result);
}
/**
* Create an extended version of this (partial) match.
* @param match Another partial match for the next matched vertices.
* @return The extended match object.
*/
public Match append(Match match) {
byte[] bytes1 = getBytes();
byte[] bytes2 = match.getBytes();
bytes1 = Arrays.copyOf(bytes1, bytes1.length + bytes2.length - PREAMBLE);
System.arraycopy(bytes2, PREAMBLE,
bytes1, bytes1.length - bytes2.length + PREAMBLE, bytes2.length - PREAMBLE);
return new Match(bytes1);
}
/**
* Remove a vertex ID of a matched node.
* @param vertexIndex Index of the vertex ID.
* @return The new match.
*/
public Match remove(int vertexIndex) {
byte[] bytes = getBytes();
int d = PREAMBLE;
for (int i = 0; i < vertexIndex; i++) {
if (d >= bytes.length) {
return null;
}
d += bytes[d] + 1;
}
if (d >= bytes.length) {
return null;
}
byte[] result = Arrays.copyOf(bytes, bytes.length - bytes[d] - 1);
if (d < result.length) {
System.arraycopy(bytes, d + 1 + bytes[d],
result, d, result.length - d);
}
return new Match(result);
}
/**
* Create a copy of this match.
* @return The copy.
*/
public Match copy() {
return new Match(getBytes());
}
/**
* Pretty-print this match.
* @return The printed string.
*/
@Override
public String toString() {
byte[] bytes = getBytes();
StringBuffer result = new StringBuffer();
int i = PREAMBLE;
while (i < bytes.length) {
int len = bytes[i++];
result.append("[");
for (int j = 0; j < len; j++) {
result.append(bytes[i + j]);
if (j < len - 1) {
result.append(",");
}
}
result.append("]");
i += len;
if (i < bytes.length - 1) {
result.append(",");
}
}
if (bytes.length > 0) {
return "s:" + getSegment() + ",m:" + getMicrostep() + ":[" + result + "]";
} else {
return "[" + result + "]";
}
}
}
/**
* Henshin application stack.
*/
public static class ApplicationStack extends Bytes {
/**
* Default constructor.
*/
public ApplicationStack() {
super();
}
/**
* Extra constructor.
* @param data The data.
*/
public ApplicationStack(byte[] data) {
super(data);
}
/**
* Get the size of this application stack.
* @return the size of this application stack.
*/
public int getStackSize() {
return (getBytes().length / INT_LENGTH) / 3;
}
/**
* Get the unit index at an absolute position.
* @param position An absolute position in the stack.
* @return the unit index or -1.
*/
public int getUnit(int position) {
IntBuffer intBuf = ByteBuffer.wrap(getBytes()).asIntBuffer();
if (position < 0 || position * 3 >= intBuf.limit()) {
return -1;
}
return intBuf.get(position * 3);
}
/**
* Get the segment index at an absolute position.
* @param position An absolute position in the stack.
* @return the segment index or -1.
*/
public int getSegment(int position) {
IntBuffer intBuf = ByteBuffer.wrap(getBytes()).asIntBuffer();
if (position < 0 || (position * 3) + 1 >= intBuf.limit()) {
return -1;
}
return intBuf.get((position * 3) + 1);
}
/**
* Get the microstep at an absolute position.
* @param position An absolute position in the stack.
* @return the microstp or -1.
*/
public int getMicrostep(int position) {
IntBuffer intBuf = ByteBuffer.wrap(getBytes()).asIntBuffer();
if (position < 0 || (position * 3) + 2 >= intBuf.limit()) {
return -1;
}
return intBuf.get((position * 3) + 2);
}
/**
* Get the unit index at the last position.
* @return the unit index or -1.
*/
public int getLastUnit() {
return getUnit(getStackSize() - 1);
}
/**
* Get the segment index at the last position.
* @return the segment index or -1.
*/
public int getLastSegment() {
return getSegment(getStackSize() - 1);
}
/**
* Get the microstep at the last position.
* @return the microstep or -1.
*/
public int getLastMicrostep() {
return getMicrostep(getStackSize() - 1);
}
/**
* Create an extended version of this application stack.
* @param unit The new unit index.
* @param segment The new segment index.
* @param microstep The new microstep.
* @return The extended version of this application stack.
*/
public ApplicationStack append(int unit, int segment, int microstep) {
byte[] bytes = getBytes();
bytes = Arrays.copyOf(bytes, bytes.length + (INT_LENGTH * 3));
IntBuffer intBuffer = ByteBuffer.wrap(bytes).asIntBuffer();
intBuffer.put((bytes.length / INT_LENGTH) - 3, unit);
intBuffer.put((bytes.length / INT_LENGTH) - 2, segment);
intBuffer.put((bytes.length / INT_LENGTH) - 1, microstep);
return new ApplicationStack(bytes);
}
/**
* Create a new version of this application stack without the last entry.
* @return The new version of this application stack.
*/
public ApplicationStack removeLast() {
byte[] bytes = getBytes();
bytes = Arrays.copyOf(bytes,
Math.max(0, bytes.length - (INT_LENGTH * 3)));
return new ApplicationStack(bytes);
}
}
/**
* Aggregator class for application stacks.
*/
public static class ApplicationStackAggregator extends
BasicAggregator<ApplicationStack> {
@Override
public void aggregate(ApplicationStack stack) {
// no action
}
@Override
public ApplicationStack createInitialValue() {
return new ApplicationStack();
}
}
/**
* Henshin input format.
*/
public static class InputFormat extends
TextVertexInputFormat<VertexId, ByteWritable, ByteWritable> {
@Override
public TextVertexReader createVertexReader(InputSplit split,
TaskAttemptContext context) {
return new InputReader();
}
/**
* Henshin input reader.
*/
class InputReader extends
TextVertexReaderFromEachLineProcessedHandlingExceptions<JSONArray,
JSONException> {
@Override
protected JSONArray preprocessLine(Text line) throws JSONException {
return new JSONArray(line.toString());
}
@Override
protected VertexId getId(JSONArray jsonVertex)
throws JSONException, IOException {
return jsonArrayToVertexId(jsonVertex.getJSONArray(0));
}
/**
* Convert a JSON array to a VertexId object.
* @param jsonArray The JSON array to be converted.
* @return The corresponding VertexId.
*/
private VertexId jsonArrayToVertexId(JSONArray jsonArray)
throws JSONException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (int i = 0; i < jsonArray.length(); i++) {
int value = jsonArray.getInt(i);
if (value < 256) {
out.write(value);
} else {
out.write(value >> 24);
out.write(value >> 16);
out.write(value >> 8);
out.write(value);
}
}
return new VertexId(out.toByteArray());
}
@Override
protected ByteWritable getValue(JSONArray jsonVertex)
throws JSONException, IOException {
return new ByteWritable((byte) jsonVertex.getInt(1));
}
@Override
protected Iterable<Edge<VertexId, ByteWritable>> getEdges(
JSONArray jsonVertex) throws JSONException, IOException {
JSONArray jsonEdgeArray = jsonVertex.getJSONArray(2);
List<Edge<VertexId, ByteWritable>> edges =
new ArrayList<>(jsonEdgeArray.length());
for (int i = 0; i < jsonEdgeArray.length(); ++i) {
JSONArray jsonEdge = jsonEdgeArray.getJSONArray(i);
edges.add(EdgeFactory.create(
jsonArrayToVertexId(jsonEdge.getJSONArray(0)),
new ByteWritable((byte) jsonEdge.getInt(1))));
}
return edges;
}
@Override
protected Vertex<VertexId, ByteWritable, ByteWritable>
handleException(Text line, JSONArray jsonVertex, JSONException e) {
throw new IllegalArgumentException(
"Couldn't get vertex from line " + line, e);
}
}
}
/**
* Henshin output format.
*/
public static class OutputFormat extends
TextVertexOutputFormat<VertexId, ByteWritable, ByteWritable> {
@Override
public TextVertexWriter createVertexWriter(TaskAttemptContext context)
throws IOException, InterruptedException {
return new OutputWriter();
}
/**
* Henshin output writer.
*/
class OutputWriter extends TextVertexWriterToEachLine {
@Override
protected Text convertVertexToLine(
Vertex<VertexId, ByteWritable, ByteWritable> vertex)
throws IOException {
JSONArray vertexArray = new JSONArray();
JSONArray idArray = new JSONArray();
byte[] id = vertex.getId().getBytes();
for (int i = 0; i < id.length; i++) {
idArray.put(id[i]);
}
vertexArray.put(idArray);
vertexArray.put(vertex.getValue().get());
JSONArray allEdgesArray = new JSONArray();
for (Edge<VertexId, ByteWritable> edge : vertex.getEdges()) {
JSONArray edgeArray = new JSONArray();
JSONArray targetIdArray = new JSONArray();
byte[] targetId = edge.getTargetVertexId().getBytes();
for (int i = 0; i < targetId.length; i++) {
targetIdArray.put(targetId[i]);
}
edgeArray.put(targetIdArray);
edgeArray.put(edge.getValue().get());
allEdgesArray.put(edgeArray);
}
vertexArray.put(allEdgesArray);
return new Text(vertexArray.toString());
}
}
}
}