/*
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
*
* Copyright 2011, 2012 Peter Güttinger
*
*/
package ch.njol.skript.expressions;
import java.lang.reflect.Array;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;
import ch.njol.skript.Skript;
import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.classes.Parser;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.ParseContext;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.VariableString;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.localization.Language;
import ch.njol.skript.log.ErrorQuality;
import ch.njol.skript.log.LogEntry;
import ch.njol.skript.log.ParseLogHandler;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.util.Kleenean;
import ch.njol.util.NonNullPair;
/**
* @author Peter Güttinger
*/
@Name("Parse")
@Description({"Parses text as a given type, or as a given pattern.",
"This expression can be used in two different ways: One which parses the entire text as a single instance of a type, e.g. as a number, " +
"and one that parses the text according to a pattern.",
"If the given text could not be parsed, this expression will return nothing and the <a href='#ExprParseError'>parse error</a> will be set if some information is available.",
"Some notes about parsing with a pattern:",
"- The pattern must be a <a href='../patterns/'>Skript pattern</a>, " +
"e.g. percent signs are used to define where to parse which types, e.g. put a %number% or %items% in the pattern if you expect a number or some items there.",
"- You <i>have to</i> save the expression's value in a list variable, e.g. <code>set {parsed::*} to message parsed as \"...\"</code>.",
"- The list variable will contain the parsed values from all %types% in the pattern in order. If a type was plural, e.g. %items%, the variable's value at the respective index will be a list variable," +
" e.g. the values will be stored in {parsed::1::*}, not {parsed::1}."})
@Examples({"set {var} to line 1 parsed as number",
"on chat:",
" set {var::*} to message parsed as \"buying %items% for %money%\"",
" if parse error is set:",
" message \"%parse error%\"",
" else if {var::*} is set:",
" cancel event",
" remove {var::2} from the player's balance",
" give {var::1::*} to the player"})
@Since("2.0")
public class ExprParse extends SimpleExpression<Object> {
static {
Skript.registerExpression(ExprParse.class, Object.class, ExpressionType.COMBINED,
"%string% parsed as (%-*classinfo%|\"<.*>\")");
}
@Nullable
static String lastError = null;
@SuppressWarnings("null")
private Expression<String> text;
@Nullable
private String pattern;
@Nullable
private boolean[] plurals;
@Nullable
private ClassInfo<?> c;
@SuppressWarnings({"unchecked", "null"})
@Override
public boolean init(final Expression<?>[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) {
text = (Expression<String>) exprs[0];
if (exprs[1] == null) {
String pattern = "" + parseResult.regexes.get(0).group();
if (!VariableString.isQuotedCorrectly(pattern, false)) {
Skript.error("Invalid amount and/or placement of double quotes in '" + pattern + "'", ErrorQuality.SEMANTIC_ERROR);
return false;
}
// escape '¦'
final StringBuilder b = new StringBuilder(pattern.length());
for (int i = 0; i < pattern.length(); i++) {
final char c = pattern.charAt(i);
if (c == '\\') {
b.append(c);
b.append(pattern.charAt(i + 1));
i++;
} else if (c == '¦') {
b.append("\\¦");
} else {
b.append(c);
}
}
pattern = "" + b.toString();
final NonNullPair<String, boolean[]> p = SkriptParser.validatePattern(pattern);
if (p == null)
return false;
this.pattern = p.getFirst();
plurals = p.getSecond();
} else {
c = ((Literal<ClassInfo<?>>) exprs[1]).getSingle();
final Parser<?> p = c.getParser();
if (p == null || !p.canParse(ParseContext.COMMAND)) { // TODO special parse context?
Skript.error("Text cannot be parsed as " + c.getName().withIndefiniteArticle(), ErrorQuality.SEMANTIC_ERROR);
return false;
}
}
return true;
}
@SuppressWarnings("null")
@Override
@Nullable
protected Object[] get(final Event e) {
final String t = text.getSingle(e);
if (t == null)
return null;
final ParseLogHandler h = SkriptLogger.startParseLogHandler();
try {
if (c != null) {
final Parser<?> p = c.getParser();
assert p != null; // checked in init()
final Object o = p.parse(t, ParseContext.COMMAND);
if (o != null) {
final Object[] one = (Object[]) Array.newInstance(c.getC(), 1);
one[0] = o;
return one;
}
} else {
assert pattern != null && plurals != null;
final ParseResult r = SkriptParser.parse(t, pattern);
if (r != null) {
assert plurals.length == r.exprs.length;
final Object[] os = new Object[r.exprs.length];
for (int i = 0; i < os.length; i++)
os[i] = plurals[i] ? r.exprs[i].getArray(null) : r.exprs[i].getSingle(null);
return os;
}
}
final LogEntry err = h.getError();
lastError = err != null ? err.getMessage() : null;
return null;
} finally {
h.clear();
h.printLog();
}
}
@Override
public boolean isSingle() {
return pattern == null;
}
@Override
public Class<? extends Object> getReturnType() {
return c != null ? c.getC() : Object[].class;
}
@Override
public String toString(final @Nullable Event e, final boolean debug) {
return text.toString(e, debug) + " parsed as " + (c != null ? c.toString(Language.F_INDEFINITE_ARTICLE) : pattern);
}
}