Skip to content
This repository has been archived by the owner on Jan 27, 2025. It is now read-only.

Commit

Permalink
Initial implementation of Record class. (#64)
Browse files Browse the repository at this point in the history
Still exposes internal map during transitional period.
  • Loading branch information
blackwinter committed Nov 12, 2021
1 parent ecc0c15 commit ccc47cb
Show file tree
Hide file tree
Showing 6 changed files with 619 additions and 144 deletions.
181 changes: 75 additions & 106 deletions org.metafacture.fix/src/main/java/org/metafacture/metafix/FixMethod.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.metafacture.metafix;

import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Stream;

Expand Down Expand Up @@ -45,34 +44,31 @@ enum Quantifier {

all {
@Override
public boolean test(final Map<String, Object> record, final FixPredicate p,
final List<String> params) {
public boolean test(final Record record, final FixPredicate p, final List<String> params) {
return test(record, params.get(0), s -> s.allMatch(p.of(params.get(1))));
}

},
any {
@Override
public boolean test(final Map<String, Object> record, final FixPredicate p,
final List<String> params) {
public boolean test(final Record record, final FixPredicate p, final List<String> params) {
return test(record, params.get(0), s -> s.anyMatch(p.of(params.get(1))));
}
},
none {
@Override
public boolean test(final Map<String, Object> record, final FixPredicate p,
final List<String> params) {
final Object fieldValue = FixMethod.find(record, FixMethod.split(params.get(0)));
public boolean test(final Record record, final FixPredicate p, final List<String> params) {
final Object fieldValue = FixMethod.find(record.temporarilyGetMap(), FixMethod.split(params.get(0)));
final String valueToTest = params.get(1);
return fieldValue == null || Metafix.asList(fieldValue).stream().noneMatch(p.of(valueToTest));
}
};

boolean test(final Map<String, Object> record, final String fieldName, final Predicate<Stream<Object>> f) {
final Object value = FixMethod.find(record, FixMethod.split(fieldName));
boolean test(final Record record, final String fieldName, final Predicate<Stream<Object>> f) {
final Object value = FixMethod.find(record.temporarilyGetMap(), FixMethod.split(fieldName));
return value != null && f.test(Metafix.asList(value).stream());
}

abstract boolean test(Map<String, Object> record, FixPredicate p, List<String> params);
abstract boolean test(Record record, FixPredicate p, List<String> params);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public class Metafix implements StreamPipe<StreamReceiver> {
private static final Logger LOG = LoggerFactory.getLogger(Metafix.class);

// TODO: Use SimpleRegexTrie / WildcardTrie for wildcard, alternation and character class support
private Map<String, Object> currentRecord = new LinkedHashMap<>();
private Record currentRecord = new Record();
private Fix fix;
private final List<Expression> expressions = new ArrayList<>();
private Map<String, String> vars = NO_VARS;
Expand Down Expand Up @@ -114,7 +114,7 @@ private void buildPipeline(final Reader fixDef, final Map<String, String> theVar

@Override
public void startRecord(final String identifier) {
currentRecord = new LinkedHashMap<>();
currentRecord = new Record();
LOG.debug("Start record: {}", identifier);
flattener.startRecord(identifier);
entityCountStack.clear();
Expand All @@ -135,11 +135,13 @@ public void endRecord() {
LOG.debug("End record, walking fix: {}", currentRecord);
final RecordTransformer transformer = new RecordTransformer(currentRecord, vars, fix);
currentRecord = transformer.transform();
if (!currentRecord.containsKey("__reject")) {
if (!currentRecord.getReject()) {
outputStreamReceiver.startRecord(recordIdentifier);
LOG.debug("Sending results to {}", outputStreamReceiver);
currentRecord.keySet().stream().filter(k -> !k.startsWith("_")).forEach(k -> {
emit(k, currentRecord.get(k));
currentRecord.forEach((k, v) -> {
if (!k.startsWith("_")) {
emit(k, v);
}
});
outputStreamReceiver.endRecord();
}
Expand Down Expand Up @@ -184,7 +186,7 @@ public void startEntity(final String name) {
entities.size() <= currentEntityIndex ? null : entities.get(currentEntityIndex);
entityCountStack.push(Integer.valueOf(entityCount));
flattener.startEntity(name);
entities.add(currentEntity(name, previousEntity == null && entities.size() >= 0 ? currentRecord : previousEntity));
entities.add(currentEntity(name, previousEntity != null ? previousEntity : currentRecord.temporarilyGetMap()));
}

private Map<String, Object> currentEntity(final String name, final Map<String, Object> previousEntity) {
Expand All @@ -197,7 +199,7 @@ private Map<String, Object> currentEntity(final String name, final Map<String, O
}
else {
currentEntity = new LinkedHashMap<>();
add(previousEntity != null ? previousEntity : currentRecord, name, currentEntity);
add(previousEntity != null ? previousEntity : currentRecord.temporarilyGetMap(), name, currentEntity);
}
return currentEntity;
}
Expand All @@ -214,7 +216,7 @@ public void literal(final String name, final String value) {
final Integer currentEntityIndex = entityCountStack.peek() - 1;
final Map<String, Object> currentEntity = currentEntityIndex < 0 ||
entities.size() <= currentEntityIndex ? null : entities.get(currentEntityIndex);
add(currentEntity != null ? currentEntity : currentRecord, name, value);
add(currentEntity != null ? currentEntity : currentRecord.temporarilyGetMap(), name, value);
// TODO: keep flattener as option?
// flattener.literal(name, value);
}
Expand Down Expand Up @@ -249,20 +251,16 @@ public Map<String, String> getVars() {
return vars;
}

public Map<String, Object> getCurrentRecord() {
public Record getCurrentRecord() {
return currentRecord;
}

static void addAll(final Map<String, Object> record, final String fieldName, final List<String> values) {
values.forEach(value -> {
add(record, fieldName, value);
});
values.forEach(value -> add(record, fieldName, value));
}

static void addAll(final Map<String, Object> record, final Map<String, Object> values) {
values.entrySet().forEach(value -> {
add(record, value.getKey(), value.getValue());
});
values.entrySet().forEach(value -> add(record, value.getKey(), value.getValue()));
}

static void add(final Map<String, Object> record, final String name, final Object newValue) {
Expand Down
181 changes: 181 additions & 0 deletions org.metafacture.fix/src/main/java/org/metafacture/metafix/Record.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright 2021 hbz NRW
*
* Licensed 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.metafacture.metafix;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;

/**
* Represents a metadata record, i.e., a mapping of fields and values.
*/
public class Record {

private static final String EMPTY = "";

private final Map<String, Object> map = new LinkedHashMap<>();

private boolean reject;

/**
* Creates an empty instance of {@link Record}.
*/
public Record() {
}

/**
* Returns a shallow clone of this record.
*
* @return a new record pre-populated with all entries from this record
*/
public Record shallowClone() {
final Record clone = new Record();

clone.setReject(reject);
forEach(clone::put);

return clone;
}

/**
* Flags whether this record should be rejected.
*
* @param reject true if this record should not be emitted, false otherwise
*/
public void setReject(final boolean reject) {
this.reject = reject;
}

/**
* Checks whether this record should be rejected.
*
* @return true if this record should not be emitted, false otherwise
*/
public boolean getReject() {
return reject;
}

/**
* Checks whether this record contains the metadata field.
*
* @param field the field name
* @return true if this record contains the metadata field, false otherwise
*/
public boolean containsField(final String field) {
return map.containsKey(field);
}

/**
* Checks whether this record is empty.
*
* @return true if this record is empty, false otherwise
*/
public boolean isEmpty() {
return map.isEmpty();
}

/**
* Gets the number of field/value pairs in this record.
*
* @return the number of field/value pairs in this record
*/
public int size() {
return map.size();
}

/**
* Adds a field/value pair to this record, provided it's not {@code null}.
*
* @param field the field name
* @param value the metadata value
*/
public void put(final String field, final Object value) {
if (value != null) {
map.put(field, value);
}
}

/**
* {@link #put(String, Object) Replaces} a field/value pair in this record,
* provided the field name is already {@link #containsField(String) present}.
*
* @param field the field name
* @param value the metadata value
*/
public void replace(final String field, final Object value) {
if (containsField(field)) {
put(field, value);
}
}

/**
* Retrieves the field value from this record.
*
* @param field the field name
* @return the metadata value
*/
public Object get(final String field) {
return map.get(field);
}

/**
* Removes the given field/value pair from this record.
*
* @param field the field name
*/
public void remove(final String field) {
map.remove(field);
}

/**
* Retains only the given field/value pairs in this record.
*
* @param fields the field names
*/
public void retainFields(final Collection<String> fields) {
map.keySet().retainAll(fields);
}

/**
* Removes all field/value pairs from this record whose value is empty.
*/
public void removeEmptyValues() {
map.values().removeIf(EMPTY::equals);
}

/**
* Iterates over all field/value pairs in this record.
*
* @param consumer the action to be performed for each field/value pair
*/
public void forEach(final BiConsumer<String, Object> consumer) {
map.forEach(consumer);
}

@Override
public String toString() {
// TODO: Improve string representation? Include reject status, etc.?
return map.toString();
}

// TODO: Replace map accesses with record operations!
public Map<String, Object> temporarilyGetMap() {
return map;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@
import org.metafacture.metafix.fix.Options;
import org.metafacture.metafix.fix.Unless;

import com.google.common.collect.ImmutableMap;
import org.eclipse.emf.common.util.EList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand All @@ -51,21 +49,21 @@ class RecordTransformer {
private static final Logger LOG = LoggerFactory.getLogger(RecordTransformer.class);

private Fix fix;
private Map<String, Object> record;
private Record record;
private Map<String, String> vars;

RecordTransformer(final Map<String, Object> record, final Map<String, String> vars, final Fix fix) {
this.record = new LinkedHashMap<>(record);
RecordTransformer(final Record record, final Map<String, String> vars, final Fix fix) {
this.record = record.shallowClone();
this.vars = vars;
this.fix = fix;
}

Map<String, Object> transform() {
Record transform() {
processSubexpressions(fix.getElements());
return record;
}

Map<String, Object> getRecord() {
Record getRecord() {
return record;
}

Expand Down Expand Up @@ -94,16 +92,21 @@ else if (sub instanceof Unless) {
private void processBind(final Do theDo, final EList<String> params) {
if (theDo.getName().equals("list")) { // TODO impl multiple binds via FixBind enum
final Map<String, String> options = options(theDo.getOptions());
final Map<String, Object> fullRecord = new LinkedHashMap<>(record);
final Object values = FixMethod.find(record, FixMethod.split(options.get("path")));
final Record fullRecord = record.shallowClone();
final Object values = FixMethod.find(record.temporarilyGetMap(), FixMethod.split(options.get("path")));

Metafix.asList(values).stream().filter(val -> val != null).forEach(val -> {
// for each val, bind the current record/scope/context to the given var name:
record = new LinkedHashMap<>(ImmutableMap.of(options.get("var"), val));
record = new Record();
record.put(options.get("var"), val);

processSubexpressions(theDo.getElements());
record.remove(options.get("var"));

// and remember the things we added while bound (this probably needs some tweaking):
Metafix.addAll(fullRecord, record);
Metafix.addAll(fullRecord.temporarilyGetMap(), record.temporarilyGetMap());
});

record = fullRecord;
}
else {
Expand Down Expand Up @@ -139,7 +142,7 @@ private boolean testConditional(final String conditional, final EList<String> pa
LOG.debug("<IF>: {} parameters: {}", conditional, params);
boolean result = false;
if ("exists".equals(conditional)) {
return record.containsKey(params.get(0));
return record.containsField(params.get(0));
}
if (!conditional.contains("_")) {
throw new IllegalArgumentException("Missing quantifier prefix (all_, any_, none_) for " + conditional);
Expand Down
Loading

0 comments on commit ccc47cb

Please sign in to comment.