Package ch.njol.skript.entity

Source Code of ch.njol.skript.entity.EntityData$EntityDataInfo

/*
*   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.entity;

import java.io.NotSerializableException;
import java.io.StreamCorruptedException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.Nullable;

import ch.njol.skript.Skript;
import ch.njol.skript.SkriptAPIException;
import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.classes.Parser;
import ch.njol.skript.classes.Serializer;
import ch.njol.skript.lang.Expression;
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.SyntaxElement;
import ch.njol.skript.lang.SyntaxElementInfo;
import ch.njol.skript.lang.util.SimpleLiteral;
import ch.njol.skript.localization.Adjective;
import ch.njol.skript.localization.Language;
import ch.njol.skript.localization.Language.LanguageListenerPriority;
import ch.njol.skript.localization.LanguageChangeListener;
import ch.njol.skript.localization.Message;
import ch.njol.skript.localization.Noun;
import ch.njol.skript.registrations.Classes;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
import ch.njol.yggdrasil.Fields;
import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable;

/**
* @author Peter Güttinger
*/
@SuppressWarnings("rawtypes")
public abstract class EntityData<E extends Entity> implements SyntaxElement, YggdrasilExtendedSerializable {// TODO extended horse support, zombie villagers // REMIND unit

  public final static String LANGUAGE_NODE = "entities";
 
  public final static Message m_age_pattern = new Message(LANGUAGE_NODE + ".age pattern");
  public final static Adjective m_baby = new Adjective(LANGUAGE_NODE + ".age adjectives.baby"),
      m_adult = new Adjective(LANGUAGE_NODE + ".age adjectives.adult");
 
  // must be here to be initialised before 'new SimpleLiteral' is called in the register block below
  private final static List<EntityDataInfo<?>> infos = new ArrayList<EntityDataInfo<?>>();
 
  public static Serializer<EntityData> serializer = new Serializer<EntityData>() {
    @Override
    public Fields serialize(final EntityData o) throws NotSerializableException {
      final Fields f = o.serialize();
      f.putObject("codeName", o.info.codeName);
      return f;
    }
   
    @Override
    public boolean canBeInstantiated(final Class<? extends EntityData> c) {
      return false;
    }
   
    @Override
    public void deserialize(final EntityData o, final Fields f) throws StreamCorruptedException {
      assert false;
    }
   
    @Override
    protected EntityData deserialize(final Fields fields) throws StreamCorruptedException, NotSerializableException {
      final String codeName = fields.getAndRemoveObject("codeName", String.class);
      if (codeName == null)
        throw new StreamCorruptedException();
      final EntityDataInfo<?> info = getInfo(codeName);
      if (info == null)
        throw new StreamCorruptedException("Invalid EntityData code name " + codeName);
      try {
        final EntityData<?> d = info.c.newInstance();
        d.deserialize(fields);
        return d;
      } catch (final InstantiationException e) {
        Skript.exception(e);
      } catch (final IllegalAccessException e) {
        Skript.exception(e);
      }
      throw new StreamCorruptedException();
    }
   
//    return getInfo((Class<? extends EntityData<?>>) d.getClass()).codeName + ":" + d.serialize();
    @SuppressWarnings("null")
    @Override
    @Deprecated
    @Nullable
    public EntityData deserialize(final String s) {
      final String[] split = s.split(":", 2);
      if (split.length != 2)
        return null;
      final EntityDataInfo<?> i = getInfo(split[0]);
      if (i == null)
        return null;
      EntityData<?> d;
      try {
        d = i.c.newInstance();
      } catch (final Exception e) {
        Skript.exception(e, "Can't create an instance of " + i.c.getCanonicalName());
        return null;
      }
      if (!d.deserialize(split[1]))
        return null;
      return d;
    }
   
    @Override
    public boolean mustSyncDeserialization() {
      return false;
    }
  };
 
  static {
    Classes.registerClass(new ClassInfo<EntityData>(EntityData.class, "entitydata")
        .user("entity ?types?")
        .name("Entity Type")
        .description("The type of an <a href='#entity'>entity</a>, e.g. player, wolf, powered creeper, etc.")
        .usage("<i>Detailed usage will be added eventually</i>")
        .examples("victim is a cow",
            "spawn a creeper")
        .since("1.3")
        .defaultExpression(new SimpleLiteral<EntityData>(new SimpleEntityData(Entity.class), true))
        .before("entitytype")
        .parser(new Parser<EntityData>() {
          @Override
          public String toString(final EntityData d, final int flags) {
            return d.toString(flags);
          }
         
          @Override
          @Nullable
          public EntityData parse(final String s, final ParseContext context) {
            return EntityData.parse(s);
          }
         
          @Override
          public String toVariableNameString(final EntityData o) {
            return "entitydata:" + o.toString();
          }
         
          @Override
          public String getVariableNamePattern() {
            return "entitydata:.+";
          }
        }).serializer(serializer));
  }
 
  private final static class EntityDataInfo<T extends EntityData<?>> extends SyntaxElementInfo<T> implements LanguageChangeListener {
    final String codeName;
    final String[] codeNames;
    final int defaultName;
    final Class<? extends Entity> entityClass;
    final Noun[] names;
   
    public EntityDataInfo(final Class<T> dataClass, final String codeName, final String[] codeNames, final int defaultName, final Class<? extends Entity> entityClass) throws IllegalArgumentException {
      super(new String[codeNames.length], dataClass);
      assert codeName != null && entityClass != null && codeNames.length > 0;
      this.codeName = codeName;
      this.codeNames = codeNames;
      this.defaultName = defaultName;
      this.entityClass = entityClass;
      this.names = new Noun[codeNames.length];
      for (int i = 0; i < codeNames.length; i++) {
        assert codeNames[i] != null;
        names[i] = new Noun(LANGUAGE_NODE + "." + codeNames[i] + ".name");
      }
     
      Language.addListener(this, LanguageListenerPriority.LATEST); // will initialise patterns, LATEST to make sure that m_age_pattern is updated before this
    }
   
    @Override
    public void onLanguageChange() {
      for (int i = 0; i < codeNames.length; i++)
        patterns[i] = Language.get(LANGUAGE_NODE + "." + codeNames[i] + ".pattern").replace("<age>", m_age_pattern.toString());
    }
   
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + codeName.hashCode();
      return result;
    }
   
    @Override
    public boolean equals(final @Nullable Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (!(obj instanceof EntityDataInfo))
        return false;
      final EntityDataInfo other = (EntityDataInfo) obj;
      if (!codeName.equals(other.codeName))
        return false;
      assert Arrays.equals(codeNames, other.codeNames);
      assert defaultName == other.defaultName;
      assert entityClass == other.entityClass;
      return true;
    }
   
  }
 
  static <E extends Entity, T extends EntityData<E>> void register(final Class<T> dataClass, final String name, final Class<E> entityClass, final String codeName) throws IllegalArgumentException {
    register(dataClass, codeName, entityClass, 0, codeName);
  }
 
  static <E extends Entity, T extends EntityData<E>> void register(final Class<T> dataClass, final String name, final Class<E> entityClass, final int defaultName, final String... codeNames) throws IllegalArgumentException {
    final EntityDataInfo<T> info = new EntityDataInfo<T>(dataClass, name, codeNames, defaultName, entityClass);
    for (int i = 0; i < infos.size(); i++) {
      if (infos.get(i).entityClass.isAssignableFrom(entityClass)) {
        infos.add(i, info);
        return;
      }
    }
    infos.add(info);
  }
 
  transient EntityDataInfo<?> info;
  protected int matchedPattern = 0;
  private Kleenean plural = Kleenean.UNKNOWN;
  private Kleenean baby = Kleenean.UNKNOWN;
 
  public EntityData() {
    for (final EntityDataInfo<?> i : infos) {
      if (getClass() == i.c) {
        info = i;
        matchedPattern = i.defaultName;
        return;
      }
    }
    throw new IllegalStateException();
  }
 
  @SuppressWarnings("null")
  @Override
  public final boolean init(final Expression<?>[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) {
    this.matchedPattern = matchedPattern;
    // plural bits (0x3): 0 = singular, 1 = plural, 2 = unknown
    final int pluralBits = parseResult.mark & 0x3;
    this.plural = pluralBits == 1 ? Kleenean.TRUE : pluralBits == 0 ? Kleenean.FALSE : Kleenean.UNKNOWN;
    // age bits (0xC): 0 = unknown, 4 = baby, 8 = adult
    final int ageBits = parseResult.mark & 0xC;
    this.baby = ageBits == 4 ? Kleenean.TRUE : ageBits == 8 ? Kleenean.FALSE : Kleenean.UNKNOWN;
    return init(Arrays.copyOf(exprs, exprs.length, Literal[].class), matchedPattern, parseResult);
  }
 
  protected abstract boolean init(final Literal<?>[] exprs, final int matchedPattern, final ParseResult parseResult);
 
  /**
   * @param c An entity's class, e.g. Player
   * @param e An actual entity, or null to get an entity data for an entity class
   * @return Whether initialisation was successful
   */
  protected abstract boolean init(@Nullable Class<? extends E> c, @Nullable E e);
 
  public abstract void set(E entity);
 
  protected abstract boolean match(E entity);
 
  public abstract Class<? extends E> getType();
 
  /**
   * Returns the super type of this entity data, e.g. 'wolf' for 'angry wolf'.
   *
   * @return The supertype of this entity data. Must not be null.
   */
  public abstract EntityData getSuperType();
 
  @Override
  public final String toString() {
    return toString(0);
  }
 
  @SuppressWarnings("null")
  protected Noun getName() {
    return info.names[matchedPattern];
  }
 
  @Nullable
  protected Adjective getAgeAdjective() {
    return baby.isTrue() ? m_baby : baby.isFalse() ? m_adult : null;
  }
 
  @SuppressWarnings("null")
  public String toString(final int flags) {
    final Noun name = info.names[matchedPattern];
    return baby.isTrue() ? m_baby.toString(name, flags) : baby.isFalse() ? m_adult.toString(name, flags) : name.toString(flags);
  }
 
  public Kleenean isPlural() {
    return plural;
  }
 
  public Kleenean isBaby() {
    return baby;
  }
 
  protected abstract int hashCode_i();
 
  @Override
  public final int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + baby.hashCode();
    result = prime * result + plural.hashCode();
    result = prime * result + matchedPattern;
    result = prime * result + info.hashCode();
    result = prime * result + hashCode_i();
    return result;
  }
 
  protected abstract boolean equals_i(EntityData<?> obj);
 
  @Override
  public final boolean equals(final @Nullable Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (!(obj instanceof EntityData))
      return false;
    final EntityData other = (EntityData) obj;
    if (baby != other.baby)
      return false;
    if (plural != other.plural)
      return false;
    if (matchedPattern != other.matchedPattern)
      return false;
    if (!info.equals(other.info))
      return false;
    return equals_i(other);
  }
 
  public final static EntityDataInfo<?> getInfo(final Class<? extends EntityData<?>> c) {
    for (final EntityDataInfo<?> i : infos) {
      if (i.c == c)
        return i;
    }
    throw new SkriptAPIException("Unregistered EntityData class " + c.getName());
  }
 
  @Nullable
  public final static EntityDataInfo<?> getInfo(final String codeName) {
    for (final EntityDataInfo<?> i : infos) {
      if (i.codeName.equals(codeName))
        return i;
    }
    return null;
  }
 
  /**
   * Prints errors.
   *
   * @param s String with optional indefinite article at the beginning
   * @return The parsed entity data
   */
  @SuppressWarnings("null")
  @Nullable
  public final static EntityData<?> parse(final String s) {
    return SkriptParser.parseStatic(Noun.stripIndefiniteArticle(s), infos.iterator(), null);
  }
 
  /**
   * Prints errors.
   *
   * @param s
   * @return The parsed entity data
   */
  @SuppressWarnings("null")
  @Nullable
  public final static EntityData<?> parseWithoutIndefiniteArticle(final String s) {
    return SkriptParser.parseStatic(s, infos.iterator(), null);
  }
 
  @Nullable
  public E spawn(final Location loc) {
    assert loc != null;
    try {
      final E e = loc.getWorld().spawn(loc, getType());
      if (e == null)
        throw new IllegalArgumentException();
      if (baby.isTrue() && e instanceof Ageable)
        ((Ageable) e).setBaby();
      set(e);
      return e;
    } catch (final IllegalArgumentException e) {
      if (Skript.testing())
        Skript.error("Can't spawn " + getType().getName());
      return null;
    }
  }
 
  @SuppressWarnings("null")
  public E[] getAll(final World... worlds) {
    assert worlds != null && worlds.length > 0 : Arrays.toString(worlds);
    final List<E> list = new ArrayList<E>();
    for (final World w : worlds) {
      for (final E e : w.getEntitiesByClass(getType()))
        if (match(e))
          list.add(e);
    }
    return list.toArray((E[]) Array.newInstance(getType(), list.size()));
  }
 
  /**
   * @param types
   * @param type
   * @param worlds worlds or null for all
   * @return All entities of this type in the given worlds
   */
  @SuppressWarnings("null")
  public final static <E extends Entity> E[] getAll(final EntityData<?>[] types, final Class<E> type, @Nullable World[] worlds) {
    assert types.length > 0;
    if (type == Player.class) {
      if (worlds == null && types.length == 1 && types[0] instanceof PlayerData && ((PlayerData) types[0]).op == 0)
        return (E[]) Bukkit.getOnlinePlayers();
      final List<Player> list = new ArrayList<Player>();
      for (final Player p : Bukkit.getOnlinePlayers()) {
        if (worlds != null && !CollectionUtils.contains(worlds, p.getWorld()))
          continue;
        for (final EntityData<?> t : types) {
          if (t.isInstance(p)) {
            list.add(p);
            break;
          }
        }
      }
      return (E[]) list.toArray(new Player[list.size()]);
    }
    final List<E> list = new ArrayList<E>();
    if (worlds == null)
      worlds = Bukkit.getWorlds().toArray(new World[0]);
    for (final World w : worlds) {
      for (final E e : w.getEntitiesByClass(type)) {
        for (final EntityData<?> t : types) {
          if (t.isInstance(e)) {
            list.add(e);
            break;
          }
        }
      }
    }
    return list.toArray((E[]) Array.newInstance(type, list.size()));
  }
 
  private static <E extends Entity> EntityData<? super E> getData(final @Nullable Class<E> c, final @Nullable E e) {
    assert c == null ^ e == null;
    assert c == null || c.isInterface();
    for (final EntityDataInfo<?> info : infos) {
      if (info.entityClass != Entity.class && (e == null ? info.entityClass.isAssignableFrom(c) : info.entityClass.isInstance(e))) {
        try {
          final EntityData<E> d = (EntityData<E>) info.c.newInstance();
          if (d.init(c, e))
            return d;
        } catch (final Exception ex) {
          throw Skript.exception(ex);
        }
      }
    }
    if (e != null) {
      return new SimpleEntityData(e);
    } else {
      assert c != null;
      return new SimpleEntityData(c);
    }
  }
 
  public static <E extends Entity> EntityData<? super E> fromClass(final Class<E> c) {
    return getData(c, null);
  }
 
  public static <E extends Entity> EntityData<? super E> fromEntity(final E e) {
    return getData(null, e);
  }
 
  public final static String toString(final Entity e) {
    return fromEntity(e).getSuperType().toString();
  }
 
  public final static String toString(final Class<? extends Entity> c) {
    return fromClass(c).getSuperType().toString();
  }
 
  public final static String toString(final Entity e, final int flags) {
    return fromEntity(e).getSuperType().toString(flags);
  }
 
  public final static String toString(final Class<? extends Entity> c, final int flags) {
    return fromClass(c).getSuperType().toString(flags);
  }
 
  @SuppressWarnings("unchecked")
  public final boolean isInstance(final @Nullable Entity e) {
    if (e == null)
      return false;
    if (!baby.isUnknown() && e instanceof Ageable && ((Ageable) e).isAdult() != baby.isFalse())
      return false;
    return getType().isInstance(e) && match((E) e);
  }
 
  public abstract boolean isSupertypeOf(EntityData<?> e);
 
  @Override
  public Fields serialize() throws NotSerializableException {
    return new Fields(this);
  }
 
  @Override
  public void deserialize(final Fields fields) throws StreamCorruptedException, NotSerializableException {
    fields.setFields(this);
  }
 
  @Deprecated
  protected boolean deserialize(final String s) {
    return false;
  }
 
}
TOP

Related Classes of ch.njol.skript.entity.EntityData$EntityDataInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.