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

Commit

Permalink
Handle and access repeated entities as arrays of hashes (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
fsteeg committed Nov 25, 2021
1 parent 7b0cc12 commit 5fa6e03
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public Predicate<Value> of(final String string) {
equal {
@Override
public Predicate<Value> of(final String string) {
return v -> v.toString().equals(string);
return v -> v.toString().equals(string);
}
},
match {
Expand Down
18 changes: 3 additions & 15 deletions metafix/src/main/java/org/metafacture/metafix/Metafix.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,21 +182,9 @@ 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 ? previousEntity : currentRecord));
}

private Value.Hash currentEntity(final String name, final Value.Hash previousEntity) {
final Value existingValue = previousEntity != null ? previousEntity.get(name) : null;
final Value.Hash currentEntity;
if (existingValue != null && existingValue.isHash()) {
currentEntity = previousEntity.get(name).asHash();
}
else {
final Value value = Value.newHash();
currentEntity = value.asHash();
(previousEntity != null ? previousEntity : currentRecord).add(name, value);
}
return currentEntity;
final Value value = Value.newHash();
(previousEntity != null ? previousEntity : currentRecord).add(name, value);
entities.add(value.asHash());
}

@Override
Expand Down
239 changes: 167 additions & 72 deletions metafix/src/main/java/org/metafacture/metafix/Value.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
Expand All @@ -33,6 +34,8 @@
*/
public class Value {

private static final String ASTERISK = "*";

private final Array array;
private final Hash hash;
private final String string;
Expand Down Expand Up @@ -203,14 +206,7 @@ public Value asList(final Consumer<Array> consumer) {
}

public Value merge(final Value value) {
if (isHash() && value.isHash()) {
final Hash asHash = asHash();
value.asHash().forEach(asHash::put);
return this;
}
else {
return asList(a1 -> value.asList(a2 -> a2.forEach(a1::add)));
}
return asList(a1 -> value.asList(a2 -> a2.forEach(a1::add)));
}

@Override
Expand Down Expand Up @@ -239,6 +235,10 @@ public String toString() {
return result;
}

static String[] tail(final String[] fields) {
return Arrays.copyOfRange(fields, 1, fields.length);
}

enum Type {
Array,
Hash,
Expand Down Expand Up @@ -300,17 +300,61 @@ public void remove(final int index) {
list.remove(index);
}

private void removeNested(final String[] fields, final Value value) {
if (fields.length > 1 && isNumber(fields[1])) {
final int index = Integer.parseInt(fields[1]) - 1;
if (index >= 0 && index < value.asArray().size()) {
if (value.asArray().get(index).isString()) {
value.asArray().remove(index);
}
private void removeNested(final String[] fields) {
if (fields.length >= 1 && fields[0].equals(ASTERISK)) {
for (int i = 0; i < size(); ++i) {
remove(i);
}
}
else if (fields.length >= 1 && isNumber(fields[0])) {
final int index = Integer.parseInt(fields[0]) - 1;
if (index >= 0 && index < size()) {
remove(index);
}
}
}

public Value find(final String[] fields) {
Value result = null;
if (fields.length > 0) {
if (fields[0].equals(ASTERISK)) {
result = find(tail(fields));
}
else if (isNumber(fields[0])) {
final int index = Integer.parseInt(fields[0]) - 1;
if (index >= 0 && index < size()) {
final Value value = get(index);
// TODO: move impl into enum elements, here call only value.find
if (value != null) {
switch (value.type) {
case Hash:
result = value.asHash().find(tail(fields));
break;
case Array:
result = find(tail(fields));
break;
case String:
result = value;
break;
default:
break;
}
}
}
}
else {
final Value newResult = newArray();
forEach(c -> {
newResult.asArray().add(c.asHash().find(fields[0])); /* TODO: non-hash */
});
result = newResult;
}
}
else {
result = new Value(this);
}
return result;
}
}

/**
Expand Down Expand Up @@ -410,30 +454,37 @@ private Value find(final String[] fields) {
final String field = fields[0];

return fields.length == 1 || !containsField(field) ? get(field) :
findNested(field, Arrays.copyOfRange(fields, 1, fields.length));
findNested(field, tail(fields));
}

private Value findNested(final String field, final String[] remainingFields) {
final Value value = get(field);

if (value.isArray()) {
if (remainingFields.length > 0 && isNumber(remainingFields[0])) {
final int index = Integer.parseInt(remainingFields[0]) - 1;
if (index >= 0 && index < value.asArray().size()) {
final Value nestedValue = value.asArray().get(index);
if (nestedValue.isString()) {
return nestedValue;
Value result = null;
if (value != null) {
switch (value.type) {
case Array:
if (remainingFields[0].equals(ASTERISK)) {
result = value.asArray().find(tail(remainingFields));
}
// TODO: array of maps, like in insert nested
}
else {
result = value.asArray().find(remainingFields);
}
break;
case Hash:
if (remainingFields[0].equals(ASTERISK)) {
result = value.asHash().find(tail(remainingFields));
}
else {
result = value.asHash().find(remainingFields);
}
break;
case String:
throw new IllegalStateException("expected string, got " + value.type);
default:
throw new IllegalStateException("unexpected, got " + value.type);
}
}

if (value.isHash()) {
return value.asHash().find(remainingFields);
}

throw new IllegalStateException("expected hash, got " + value.type);
return result;
}

public Value findList(final String fieldPath, final Consumer<Array> consumer) {
Expand Down Expand Up @@ -468,52 +519,72 @@ public Value insert(final InsertMode mode, final String fieldPath, final String
private Value insert(final InsertMode mode, final String[] fields, final String newValue) {
final String field = fields[0];

if (fields.length == 1) {
if (fields.length == 1 && !fields[0].equals(ASTERISK)) {
mode.apply(this, field, newValue);
}
else {
if (!containsField(field)) {
put(field, newHash());
}

final String[] remainingFields = Arrays.copyOfRange(fields, 1, fields.length);
final String[] nestedFields = Arrays.copyOfRange(remainingFields, 1, remainingFields.length);
final Value value = get(field);

if (value.isHash()) {
value.asHash().insert(mode, remainingFields, newValue);
}
else if (value.isArray()) {
final Array array = value.asArray();

switch (remainingFields[0]) {
case APPEND_FIELD:
array.add(newHash(h -> h.insert(mode, nestedFields, newValue)));
if (value != null) {
switch (value.type) {
// TODO: move impl into enum elements, here call only value.insert
case Hash:
final String[] tail = tail(fields);
final String[] rest = tail[0].startsWith("$") ? tail(tail) : tail; // TODO: why?
value.asHash().insert(mode, rest, newValue);
break;
case LAST_FIELD:
final Value last = array.get(array.size() - 1);
if (last.isHash()) {
last.asHash().insert(mode, nestedFields, newValue);
}
case Array:
insertArray(mode, newValue, tail(fields), value.asArray());
break;
case String:
throw new IllegalStateException("expected array or hash, got " + value.type);
default:
if (isNumber(remainingFields[0])) {
array.add(new Value(newValue));
}
else {
array.add(newHash(h -> h.insert(mode, remainingFields, newValue)));
}
break;
throw new IllegalStateException("expected array or hash, got " + value.type);
}
}
else {
throw new IllegalStateException("expected array or hash, got " + value.type);
}
}

return new Value(this);
}

private void insertArray(final InsertMode mode, final String newValue, final String[] fields,
final Array array) {
switch (fields[0]) {
case ASTERISK:
break;
case APPEND_FIELD:
array.add(newHash(h -> h.insert(mode, tail(fields), newValue)));
break;
case LAST_FIELD:
if (array.size() > 0) {
final Value last = array.get(array.size() - 1);
if (last.isHash()) {
last.asHash().insert(mode, tail(fields), newValue);
}
}
break;
default:
if (isNumber(fields[0])) {
if (fields.length == 1) {
array.add(new Value(newValue));
}
if (fields.length > 1) {
final Value newHash = Value.newHash();
newHash.asHash().put(fields[1], new Value(newValue));
array.add(newHash);
}
}
else {
final String[] rem = fields;
array.add(newHash(h -> h.insert(mode, rem, newValue)));
}
break;
}
}

/**
* Removes the given field/value pair from this hash.
*
Expand All @@ -535,30 +606,55 @@ private void removeNested(final String[] fields) {
}
else if (containsField(field)) {
final Value value = get(field);
if (value.isArray()) {
value.asArray().removeNested(fields, value);
}
if (value.isHash()) {
value.asHash().removeNested(Arrays.copyOfRange(fields, 1, fields.length));
// TODO: impl and call just value.remove
if (value != null) {
switch (value.type) {
case String:
break;
case Array:
value.asArray().removeNested(tail(fields));
break;
case Hash:
value.asHash().removeNested(tail(fields));
break;
default:
break;
}
}
}
}

public void copy(final List<String> params) {
final String oldName = params.get(0);
final String newName = params.get(1);
findList(oldName, a -> a.forEach(v -> append(newName, v.toString())));
findList(oldName, a -> a.forEach(v -> appendValue(split(newName), v)));
}

private void appendValue(final String[] newName, final Value v) {
// TODO: impl and call just value.append
if (v != null) {
switch (v.type) {
case String:
append(Arrays.asList(newName).stream().collect(Collectors.joining(".")), v.asString());
break;
case Array:
break;
case Hash:
appendValue(newName, v.asHash().find(tail(newName)));
break;
default:
break;
}
}
}

public void transformFields(final List<String> params, final UnaryOperator<String> operator) {
final String field = params.get(0).replace(".*", "");
final String field = params.get(0);
final Value value = find(field);

if (value != null) {
removeNested(field);

removeNested(field.replace(".*", ""));
if (operator != null) {
value.asList(a -> a.forEach(v -> append(field, operator.apply(v.toString()))));
value.asList(a -> a.forEach(v -> append(field.replace(".*", ""), operator.apply(v.toString()))));
}
}
}
Expand Down Expand Up @@ -621,5 +717,4 @@ void apply(final Hash hash, final String field, final String value) {
}

}

}
Loading

0 comments on commit 5fa6e03

Please sign in to comment.