package org.vertexium.accumulo.iterator.model; import org.apache.accumulo.core.data.Value; import org.apache.hadoop.io.Text; import org.vertexium.accumulo.iterator.util.DataOutputStreamUtils; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.*; public abstract class ElementData { public static final byte[] HEADER = new byte[]{'V', 'E', 'R', 'T', '1'}; public static final byte TYPE_ID_VERTEX = 1; public static final byte TYPE_ID_EDGE = 2; public static final int PROP_START = 1; public static final int PROP_END = 2; public Text id; public long timestamp; public Text visibility; public List<Text> hiddenVisibilities = new ArrayList<>(); public long softDeleteTimestamp; public List<SoftDeletedProperty> softDeletedProperties = new ArrayList<>(); public List<HiddenProperty> hiddenProperties = new ArrayList<>(); public Map<String, PropertyMetadata> propertyMetadata = new HashMap<>(); public final Map<String, PropertyColumnQualifier> propertyColumnQualifiers = new HashMap<>(); public final Map<String, byte[]> propertyValues = new HashMap<>(); public final Map<String, Text> propertyVisibilities = new HashMap<>(); public final Map<String, Long> propertyTimestamps = new HashMap<>(); public final Set<String> extendedTableNames = new HashSet<>(); public void clear() { id = null; visibility = null; timestamp = 0; softDeleteTimestamp = 0; hiddenVisibilities.clear(); softDeletedProperties.clear(); hiddenProperties.clear(); propertyMetadata.clear(); propertyColumnQualifiers.clear(); propertyValues.clear(); propertyVisibilities.clear(); propertyTimestamps.clear(); extendedTableNames.clear(); } public final Value encode(EnumSet<IteratorFetchHint> fetchHints) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(out); encode(dout, fetchHints); return new Value(out.toByteArray()); } protected void encode(DataOutputStream out, EnumSet<IteratorFetchHint> fetchHints) throws IOException { encodeHeader(out); DataOutputStreamUtils.encodeText(out, id); out.writeLong(timestamp); DataOutputStreamUtils.encodeText(out, visibility); DataOutputStreamUtils.encodeTextList(out, hiddenVisibilities); encodeProperties(out, fetchHints); DataOutputStreamUtils.encodeStringSet(out, extendedTableNames); } private void encodeHeader(DataOutputStream out) throws IOException { out.write(HEADER); out.write(getTypeId()); } protected abstract byte getTypeId(); private void encodeProperties(final DataOutputStream out, EnumSet<IteratorFetchHint> fetchHints) throws IOException { iterateProperties(new PropertyDataHandler() { @Override public void handle( String propertyKey, String propertyName, byte[] propertyValue, Text propertyVisibility, long propertyTimestamp, Set<Text> propertyHiddenVisibilities, PropertyMetadata metadata ) throws IOException { out.write(PROP_START); DataOutputStreamUtils.encodeString(out, propertyKey); DataOutputStreamUtils.encodeString(out, propertyName); DataOutputStreamUtils.encodeText(out, propertyVisibility); out.writeLong(propertyTimestamp); out.writeInt(propertyValue.length); out.write(propertyValue); DataOutputStreamUtils.encodeTextList(out, propertyHiddenVisibilities); DataOutputStreamUtils.encodePropertyMetadata(out, metadata); } }, fetchHints); out.write(PROP_END); } private void iterateProperties(PropertyDataHandler propertyDataHandler, EnumSet<IteratorFetchHint> fetchHints) throws IOException { boolean includeHidden = fetchHints.contains(IteratorFetchHint.INCLUDE_HIDDEN); for (Map.Entry<String, byte[]> propertyValueEntry : propertyValues.entrySet()) { String key = propertyValueEntry.getKey(); PropertyColumnQualifier propertyColumnQualifier = propertyColumnQualifiers.get(key); String propertyKey = propertyColumnQualifier.getPropertyKey(); String propertyName = propertyColumnQualifier.getPropertyName(); byte[] propertyValue = propertyValueEntry.getValue(); Text propertyVisibility = propertyVisibilities.get(key); String propertyVisibilityString = propertyVisibility.toString(); long propertyTimestamp = propertyTimestamps.get(key); Set<Text> propertyHiddenVisibilities = getPropertyHiddenVisibilities(propertyKey, propertyName, propertyVisibilityString); if (!includeHidden && isHidden(propertyKey, propertyName, propertyVisibilityString)) { continue; } if (isPropertyDeleted(propertyKey, propertyName, propertyTimestamp, propertyVisibility)) { continue; } PropertyMetadata metadata = propertyMetadata.get(key); propertyDataHandler.handle(propertyKey, propertyName, propertyValue, propertyVisibility, propertyTimestamp, propertyHiddenVisibilities, metadata); } } public Iterable<Property> getProperties(EnumSet<IteratorFetchHint> fetchHints) { final List<Property> results = new ArrayList<>(); try { iterateProperties(new PropertyDataHandler() { @Override public void handle( String propertyKey, String propertyName, byte[] propertyValue, Text propertyVisibility, long propertyTimestamp, Set<Text> propertyHiddenVisibilities, PropertyMetadata metadata ) throws IOException { results.add(new Property( propertyKey, propertyName, propertyValue, propertyVisibility.toString(), propertyTimestamp, propertyHiddenVisibilities, metadata )); } }, fetchHints); } catch (IOException ex) { throw new VertexiumAccumuloIteratorException("Could not get properties", ex); } return results; } private interface PropertyDataHandler { void handle(String propertyKey, String propertyName, byte[] propertyValue, Text propertyVisibility, long propertyTimestamp, Set<Text> propertyHiddenVisibilities, PropertyMetadata metadata) throws IOException; } private Set<Text> getPropertyHiddenVisibilities(String propertyKey, String propertyName, String propertyVisibility) { Set<Text> hiddenVisibilities = null; for (HiddenProperty hiddenProperty : hiddenProperties) { if (hiddenProperty.matches(propertyKey, propertyName, propertyVisibility)) { if (hiddenVisibilities == null) { hiddenVisibilities = new HashSet<>(); } hiddenVisibilities.add(hiddenProperty.getHiddenVisibility()); } } return hiddenVisibilities; } private boolean isHidden(String propertyKey, String propertyName, String propertyVisibility) { for (HiddenProperty hiddenProperty : hiddenProperties) { if (hiddenProperty.matches(propertyKey, propertyName, propertyVisibility)) { return true; } } return false; } private boolean isPropertyDeleted(String propertyKey, String propertyName, long propertyTimestamp, Text propertyVisibility) { for (SoftDeletedProperty softDeletedProperty : softDeletedProperties) { if (softDeletedProperty.matches(propertyKey, propertyName, propertyVisibility)) { return softDeletedProperty.getTimestamp() >= propertyTimestamp; } } return false; } }