/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.internal.managed.items.serializer; import com.jcwhatever.nucleus.Nucleus; import com.jcwhatever.nucleus.internal.managed.items.meta.InternalItemMetaHandlers; import com.jcwhatever.nucleus.managed.items.meta.IItemMetaHandler; import com.jcwhatever.nucleus.managed.items.meta.ItemMetaValue; import com.jcwhatever.nucleus.managed.items.serializer.IItemStackDeserializer; import com.jcwhatever.nucleus.managed.items.serializer.InvalidItemStackStringException; import com.jcwhatever.nucleus.utils.PreCon; import com.jcwhatever.nucleus.utils.converters.Converters; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.Potion; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.annotation.Nullable; /** * Parses a string representing a single or multiple {@link org.bukkit.inventory.ItemStack}'s * serialized by {@link ItemSerializer}. * <p> * Format: MaterialName[:ByteData][;Amount][{ metaName1: "metaValue1", metaName2: "metaValue2" }] * </p> * * @see ItemSerializer * @see InternalItemMetaHandlers */ class ItemDeserializer implements IItemStackDeserializer { private final String _itemString; private StringBuilder _buffer; private int _index = 0; private List<ItemStack> _results = new ArrayList<>(5); /** * Constructor. * * @param itemString The string to parse. The string must represent one or more * {@link org.bukkit.inventory.ItemStack}'s * * @throws InvalidItemStackStringException */ public ItemDeserializer(String itemString) throws InvalidItemStackStringException { PreCon.notNull(itemString); _itemString = itemString; _buffer = new StringBuilder(itemString.length()); while (canParse()) { ItemStack item = parseItemStack(); if (item == null) break; _results.add(item); } } @Override public List<ItemStack> getResults(List<ItemStack> output) { PreCon.notNull(output); output.addAll(_results); return output; } @Override public ItemStack[] getArray() { return _results.toArray(new ItemStack[_results.size()]); } /* * Parse the next item stack in the string. * Returns null if there are no more item stacks. */ @Nullable private ItemStack parseItemStack() throws InvalidItemStackStringException { _buffer.setLength(0); if (current() == ',') next(); if (!canParse()) return null; skipEmpty(); ItemStack stack; Material material; short data = 0; int amount = 1; Potion potion; List<ItemMetaValue> metaObjects = new ArrayList<>(15); String rawMaterial = parseMaterial(); if (rawMaterial.isEmpty()) return null; material = Converters.MATERIAL.convert(rawMaterial); if (material == null) { throw new InvalidItemStackStringException(_itemString); } if (current() == ':') { next(); data = parseData(); } if (current() == ';') { next(); amount = parseAmount(); } if (current() == '{') { next(); parseMeta(metaObjects); } // handle special cases if (material == Material.POTION && (potion = Converters.POTION.convert(data)) != null) { stack = potion.toItemStack(amount); } else { // set stack data stack = new ItemStack(material, amount); stack.setDurability(data); } for (ItemMetaValue meta : metaObjects) { IItemMetaHandler handler = Nucleus.getItemMetaHandlers().getHandler(meta.getName()); if (handler == null) continue; handler.apply(stack, meta); } return stack; } /* * Parse the material name/id */ private String parseMaterial() { _buffer.setLength(0); while (canParse()) { char ch = current(); if (isEmptySpace(ch)) { next(); } else { if (ch != ':' && ch != ';' && ch != '{' && ch != ',') { _buffer.append(ch); next(); } else { break; } } } return _buffer.toString(); } /* * Parse string data (byte) value as a short (for use as durability) */ private short parseData() throws InvalidItemStackStringException { _buffer.setLength(0); while(canParse()) { char ch = current(); if (isEmptySpace(ch)) { next(); } else { if (ch != ';' && ch != '{' && ch != ',') { _buffer.append(ch); next(); } else { break; } } } String rawData = _buffer.toString(); try { return Short.parseShort(rawData); } catch (NumberFormatException e) { e.printStackTrace(); throw new InvalidItemStackStringException(_itemString); } } /* * Parse the number of items in the ItemStack. */ private int parseAmount() throws InvalidItemStackStringException { _buffer.setLength(0); while(canParse()) { char ch = current(); if (isEmptySpace(ch)) { next(); } else { if (ch == '{' || ch == ',') { break; } else { _buffer.append(ch); next(); } } } String rawData = _buffer.toString(); try { return Integer.parseInt(rawData); } catch (NumberFormatException e) { e.printStackTrace(); throw new InvalidItemStackStringException(_itemString); } } /* * Parse extra meta data */ private void parseMeta(Collection<ItemMetaValue> metaObjects) throws InvalidItemStackStringException { while (canParse()) { String metaName = parseMetaName(); if (metaName.isEmpty()) return; String metaValue = parseMetaValue(); metaObjects.add(new ItemMetaValue(metaName, metaValue)); } } /* * Parse a meta data value name. */ private String parseMetaName() { _buffer.setLength(0); while (canParse()) { char ch = current(); if (isEmptySpace(ch) || ch == ',') { next(); } else { if (ch != ':' && ch != '}') { _buffer.append(ch); next(); } else { next(); break; } } } return _buffer.toString(); } /* * Parse a meta data value. */ private String parseMetaValue() throws InvalidItemStackStringException { _buffer.setLength(0); boolean isParsingLiteral = false; while (canParse()) { char ch = current(); if (isParsingLiteral) { if (ch == '\\') { char escaped = peek(1); if (escaped == '\"') { _buffer.append(escaped); next(); next(); } else { _buffer.append(ch); next(); } } else if (ch == '"') { next(); break; } else { _buffer.append(ch); next(); } } else { if (isEmptySpace(ch) || ch == ':') { next(); } else if (ch == '"') { isParsingLiteral = true; next(); } else { throw new InvalidItemStackStringException(_itemString); } } } return _buffer.toString(); } /* * Determine if parsing can continue. */ private boolean canParse() { return _index < _itemString.length(); } /* * Determine if a character is an empty space. */ private boolean isEmptySpace(char ch) { return " \r\n\t".indexOf(ch) != -1; } /* * Get the current character. */ private char current() { if (!canParse()) return 0; return _itemString.charAt(_index); } /* * Get the next character and increment the index position. */ private char next() { char ch = _itemString.charAt(_index); _index++; return ch; } /* * Get a character forward from the current position without incrementing * the index. */ private char peek(int amount) { return _itemString.charAt(_index + amount); } /* * Skip all empty characters. */ private void skipEmpty() { while (canParse()) { if (isEmptySpace(current())) next(); else break; } } }