/*
* 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-2014 Peter Güttinger
*
*/
package ch.njol.skript.expressions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.eclipse.jdt.annotation.Nullable;
import ch.njol.skript.Skript;
import ch.njol.skript.SkriptConfig;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.classes.Changer.ChangerUtils;
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.SkriptParser.ParseResult;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.util.Kleenean;
import ch.njol.util.Math2;
import ch.njol.util.StringUtils;
/**
* TODO make a 'line %number% of %text%' expression and figure out how to deal with signs (4 lines, delete = empty, etc...)
*
* @author joeuguce99
*/
@Name("Lore")
@Description("An item's lore.")
@Examples("set the 1st line of the item's lore to \"<orange>Excalibur 2.0\"")
@Since("2.1")
public class ExprLore extends SimpleExpression<String> {
static {
try {
ItemMeta.class.getName();
Skript.registerExpression(ExprLore.class, String.class, ExpressionType.PROPERTY,
"[the] lore of %itemstack/itemtype%", "%itemstack/itemtype%'[s] lore",
"[the] line %number% of [the] lore of %itemstack/itemtype%", "[the] line %number% of %itemstack/itemtype%'[s] lore",
"[the] %number%(st|nd|rd|th) line of [the] lore of %itemstack/itemtype%", "[the] %number%(st|nd|rd|th) line of %itemstack/itemtype%'[s] lore");
} catch (final NoClassDefFoundError e) {}
}
@Nullable
private Expression<Number> line;
@SuppressWarnings("null")
private Expression<?> item;
@SuppressWarnings({"unchecked", "null"})
@Override
public boolean init(final Expression<?>[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) {
line = exprs.length > 1 ? (Expression<Number>) exprs[0] : null;
item = exprs[exprs.length - 1];
return true;
}
@Override
public Class<? extends String> getReturnType() {
return String.class;
}
@Override
@Nullable
protected String[] get(final Event e) {
final Object i = item.getSingle(e);
final Number n = line != null ? line.getSingle(e) : null;
if (n == null && line != null)
return null;
if (i == null || i instanceof ItemStack && ((ItemStack) i).getType() == Material.AIR)
return new String[0];
final ItemMeta meta = i instanceof ItemStack ? ((ItemStack) i).getItemMeta() : (ItemMeta) ((ItemType) i).getItemMeta();
if (meta == null || !meta.hasLore())
return new String[0];
if (n == null)
return new String[] {};
final int l = n.intValue() - 1;
final List<String> lore = meta.getLore();
if (l < 0 || l >= lore.size())
return new String[0];
return new String[] {lore.get(l)};
}
@Override
public String toString(final @Nullable Event e, final boolean debug) {
return (line != null ? "the line " + line.toString(e, debug) + " of " : "") + "the lore of " + item.toString(e, debug);
}
@Override
@Nullable
public Class<?>[] acceptChange(final ChangeMode mode) {
switch (mode) {
case SET:
case DELETE:
case ADD:
case REMOVE:
case REMOVE_ALL:
if (ChangerUtils.acceptsChange(item, ChangeMode.SET, ItemStack.class, ItemType.class))
return new Class[] {String.class};
return null;
case RESET:
default:
return null;
}
}
// TODO test (especially remove)
@Override
public void change(final Event e, final @Nullable Object[] delta, final ChangeMode mode) throws UnsupportedOperationException {
final Object i = item.getSingle(e);
if (i == null || i instanceof ItemStack && ((ItemStack) i).getType() == Material.AIR)
return;
ItemMeta meta = i instanceof ItemStack ? ((ItemStack) i).getItemMeta() : (ItemMeta) ((ItemType) i).getItemMeta();
if (meta == null)
meta = Bukkit.getItemFactory().getItemMeta(Material.STONE);
final Number n = line != null ? line.getSingle(e) : null;
List<String> lore = meta.hasLore() ? new ArrayList<String>(meta.getLore()) : new ArrayList<String>();
if (n == null) {
if (line != null)
return;
switch (mode) {
case SET:
assert delta != null;
lore = Arrays.asList((String) delta[0]);
break;
case ADD:
assert delta != null;
lore.add((String) delta[0]);
break;
case DELETE:
lore = null;
break;
case REMOVE:
case REMOVE_ALL:
assert delta != null;
if (SkriptConfig.caseSensitive.value()) {
lore = Arrays.asList((mode == ChangeMode.REMOVE ? StringUtils.join(lore, "\n").replaceFirst(Pattern.quote((String) delta[0]), "") : StringUtils.join(lore, "\n").replace((CharSequence) delta[0], "")).split("\n"));
} else {
final Matcher m = Pattern.compile(Pattern.quote((String) delta[0]), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE).matcher(StringUtils.join(lore, "\n"));
lore = Arrays.asList((mode == ChangeMode.REMOVE ? m.replaceFirst("") : m.replaceAll("")).split("\n"));
}
break;
case RESET:
assert false;
break;
}
} else {
final int l = Math2.fit(0, n.intValue() - 1, 99); // TODO figure out the actual maximum
for (int j = lore.size(); j <= l; j++)
lore.add("");
switch (mode) {
case SET:
assert delta != null;
lore.set(l, (String) delta[0]);
break;
case DELETE:
lore.remove(l);
break;
case ADD:
assert delta != null;
lore.set(l, lore.get(l) + (String) delta[0]);
break;
case REMOVE:
case REMOVE_ALL:
assert delta != null;
if (SkriptConfig.caseSensitive.value()) {
lore.set(l, mode == ChangeMode.REMOVE ? lore.get(l).replaceFirst(Pattern.quote((String) delta[0]), "") : lore.get(l).replace((CharSequence) delta[0], ""));
} else {
final Matcher m = Pattern.compile(Pattern.quote((String) delta[0]), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE).matcher(lore.get(l));
lore.set(l, mode == ChangeMode.REMOVE ? m.replaceFirst("") : m.replaceAll(""));
}
break;
case RESET:
assert false;
return;
}
}
meta.setLore(lore == null || lore.size() == 0 ? null : lore);
if (i instanceof ItemStack)
((ItemStack) i).setItemMeta(meta);
else
((ItemType) i).setItemMeta(meta);
if (ChangerUtils.acceptsChange(item, ChangeMode.SET, i.getClass())) {
item.change(e, i instanceof ItemStack ? new ItemStack[] {(ItemStack) i} : new ItemType[] {(ItemType) i}, ChangeMode.SET);
} else {
item.change(e, i instanceof ItemStack ? new ItemType[] {new ItemType((ItemStack) i)} : new ItemStack[] {((ItemType) i).getRandom()}, ChangeMode.SET);
}
return;
}
@Override
public boolean isSingle() {
return true;
}
}