Skip to content

Commit

Permalink
✨ Introduce embedded value beta expression
Browse files Browse the repository at this point in the history
  • Loading branch information
Romitou committed May 21, 2022
1 parent 5fbc56c commit ec06d16
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 0 deletions.
34 changes: 34 additions & 0 deletions src/main/java/fr/romitou/mongosk/elements/MongoSKDocument.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package fr.romitou.mongosk.elements;

import fr.romitou.mongosk.LoggerHelper;
import fr.romitou.mongosk.skript.expressions.ExprMongoEmbeddedValue;
import org.bson.Document;

import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

public class MongoSKDocument {
Expand Down Expand Up @@ -48,6 +52,36 @@ public void printDebug() {
);
}

public Object getEmbeddedValue(final ExprMongoEmbeddedValue.MongoQueryElement[] queryElements) {
Object value = this.bsonDocument;
Iterator<ExprMongoEmbeddedValue.MongoQueryElement> keyIterator = Arrays.stream(queryElements).iterator();
while (keyIterator.hasNext()) {
ExprMongoEmbeddedValue.MongoQueryElement queryElement = keyIterator.next();
if (queryElement.path != null) {
if (value instanceof Document) {
value = ((Document) value).get(queryElement.path);
} else {
LoggerHelper.debug("Expected a document, but got a " + value.getClass().getSimpleName() + " instead at path '" + queryElement.path + "'.",
"Query elements: " + Arrays.toString(queryElements),
"Document: " + this.bsonDocument.toJson()
);
return null;
}
} else if (queryElement.index != null) {
if (value instanceof List) {
value = ((List<Object>) value).get(queryElement.index);
} else {
LoggerHelper.severe("Expected a list, but got a " + value.getClass().getSimpleName() + " instead at index " + queryElement.index + ".",
"Query elements: " + Arrays.toString(queryElements),
"Document: " + this.bsonDocument.toJson()
);
return null;
}
}
}
return value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package fr.romitou.mongosk.skript.expressions;

import ch.njol.skript.Skript;
import ch.njol.skript.classes.Changer;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.util.Kleenean;
import fr.romitou.mongosk.LoggerHelper;
import fr.romitou.mongosk.adapters.MongoSKAdapter;
import fr.romitou.mongosk.elements.MongoSKDocument;
import org.bukkit.event.Event;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

public class ExprMongoEmbeddedValue extends SimpleExpression<Object> {

static {
Skript.registerExpression(
ExprMongoEmbeddedValue.class,
Object.class,
ExpressionType.COMBINED,
"mongo[(sk|db)] embedded (1¦(field|value)|2¦(array|list)) [with path] %string% of %mongoskdocument%"
);
}

private Expression<String> exprFieldName;
private Expression<MongoSKDocument> exprMongoSKDocument;
private Boolean isSingle;

@SuppressWarnings("unchecked")
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, @Nonnull Kleenean isDelayed, @Nonnull SkriptParser.ParseResult parseResult) {
exprFieldName = (Expression<String>) exprs[0];
exprMongoSKDocument = (Expression<MongoSKDocument>) exprs[1];
isSingle = parseResult.mark == 1;
return true;
}

@Override
protected Object[] get(@Nonnull final Event e) {
String fieldName = exprFieldName.getSingle(e);
MongoSKDocument mongoSKDocument = exprMongoSKDocument.getSingle(e);
if (fieldName == null || mongoSKDocument == null || mongoSKDocument.getBsonDocument() == null)
return new Object[0];
MongoQueryElement[] mongoQueryElements = buildQueryElementsFromString(fieldName);
try {
Object value = mongoSKDocument.getEmbeddedValue(mongoQueryElements);
if (isSingle) {
return new Object[]{MongoSKAdapter.deserializeValue(value)};
} else {
if (value instanceof List) {
return MongoSKAdapter.deserializeValues(((List<Object>) value).toArray());
} else if (value instanceof Object[]) {
return MongoSKAdapter.deserializeValues((Object[]) value);
} else {
return new Object[0];
}
}
} catch (ClassCastException ex) {
LoggerHelper.severe("The type of item you are querying is not correct. " +
"This can happen if you want to retrieve a list, but it is a single value.",
"Document: " + mongoSKDocument.getBsonDocument().toJson(),
"Exception: " + ex.getMessage());
return new Object[0];
}
}

@Override
public Class<?>[] acceptChange(Changer.ChangeMode mode) {
return new Class[0];
}

// @Override
// public void change(@Nonnull final Event e, Object[] delta, @Nonnull Changer.ChangeMode mode) {
// String fieldName = exprFieldName.getSingle(e);
// MongoSKDocument mongoSKDocument = exprMongoSKDocument.getSingle(e);
// List<?> omega = new ArrayList<>();
// if (delta != null)
// omega = Arrays.asList(MongoSKAdapter.serializeArray(delta));
// if (fieldName == null || mongoSKDocument == null || mongoSKDocument.getBsonDocument() == null)
// return;
// switch (mode) {
// case ADD:
// try {
// ArrayList<Object> addList = new ArrayList<>(mongoSKDocument.getBsonDocument().getList(fieldName, Object.class));
// addList.addAll(omega);
// mongoSKDocument.getBsonDocument().put(fieldName, addList);
// } catch (NullPointerException ex) {
// mongoSKDocument.getBsonDocument().put(fieldName, omega);
// } catch (RuntimeException ex) {
// reportException("adding objects", fieldName, mongoSKDocument, omega, ex);
// }
// break;
// case SET:
// try {
// mongoSKDocument.getBsonDocument().put(fieldName, isSingle ? omega.get(0) : omega);
// } catch (RuntimeException ex) {
// reportException("setting field", fieldName, mongoSKDocument, omega, ex);
// }
// break;
// case REMOVE:
// try {
// ArrayList<Object> removeList = new ArrayList<>(mongoSKDocument.getBsonDocument().getList(fieldName, Object.class));
// removeList.removeAll(omega);
// mongoSKDocument.getBsonDocument().put(fieldName, removeList);
// } catch (RuntimeException ex) {
// reportException("removing objects", fieldName, mongoSKDocument, omega, ex);
// }
// break;
// case DELETE:
// try {
// mongoSKDocument.getBsonDocument().remove(fieldName);
// } catch (RuntimeException ex) {
// reportException("deleting field", fieldName, mongoSKDocument, omega, ex);
// }
// break;
// default:
// break;
// }
// }

// private void reportException(String action, String fieldName, MongoSKDocument document, List<?> omega, Exception ex) {
// LoggerHelper.severe("An error occurred during " + action + " of Mongo document: " + ex.getMessage(),
// "Field name: " + fieldName,
// "Document: " + document.getBsonDocument().toJson(),
// "Omega: " + omega
// );
// }

@Override
public boolean isSingle() {
return isSingle;
}

@Override
@Nonnull
public Class<?> getReturnType() {
return Object.class;
}

@Override
@Nonnull
public String toString(@Nullable Event e, boolean debug) {
return "mongo " + (isSingle ? "value" : "list") + " named " + exprFieldName.toString(e, debug) + " of " + exprMongoSKDocument.toString(e, debug);
}

public class MongoQueryElement {
public String path;
public Integer index;

MongoQueryElement(String path, Integer index) {
this.path = path;
this.index = index;
}

MongoQueryElement(String path) {
this(path, null);
}

MongoQueryElement(Integer index) {
this(null, index);
}

@Override
public String toString() {
return "MongoQueryElement{" +
"path='" + path + '\'' +
", index=" + index +
'}';
}
}

public MongoQueryElement[] buildQueryElementsFromString(String string) {
ArrayList<MongoQueryElement> mongoQueryElements = new ArrayList<>();
Integer arrayStartIndex = null;
Integer textStartIndex = null;
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
boolean lastIteration = i == string.length() - 1;
boolean isArrayStart = c == '[';
if (((c == '.' || isArrayStart) && (i >= 1 && string.charAt(i - 1) != '\\')) || (lastIteration && arrayStartIndex == null)) {
if (isArrayStart)
arrayStartIndex = i;
Integer textIndex = textStartIndex;
textStartIndex = null;
if (textIndex == null) {
LoggerHelper.severe("The path is not valid. Expected a field name, but found a single dot.",
"Path: " + string,
"Index: " + i);
continue;
}
String queryString = string.substring(textIndex, lastIteration ? i + 1 : i);
mongoQueryElements.add(new MongoQueryElement(queryString.replace("\\.", ".")));
} else if (arrayStartIndex == null && textStartIndex == null) {
textStartIndex = i;
} else if (c == ']') {
Integer startIndex = arrayStartIndex;
arrayStartIndex = null;
if (startIndex == null) {
LoggerHelper.severe("The path is not valid. Expected an array index, but found a closing bracket.",
"Path: " + string,
"Index: " + i);
continue;
}
Integer index = null;
try {
index = Integer.parseInt(string.substring(startIndex + 1, i));
} catch (NumberFormatException ex) {
LoggerHelper.severe("The path is not valid. Expected an integer, but found a non-integer value within the array brackets.",
"String: " + string,
"Index: " + i);
}
mongoQueryElements.add(new MongoQueryElement(index));
if (string.length() > i + 1 && string.charAt(i + 1) == '.')
i++;
}
}
return mongoQueryElements.toArray(new MongoQueryElement[0]);
}

}

0 comments on commit ec06d16

Please sign in to comment.