Package net.aufdemrand.denizen.objects

Source Code of net.aufdemrand.denizen.objects.dLocation

package net.aufdemrand.denizen.objects;

import net.aufdemrand.denizen.Settings;
import net.aufdemrand.denizen.objects.notable.Notable;
import net.aufdemrand.denizen.objects.notable.NotableManager;
import net.aufdemrand.denizen.objects.notable.Note;
import net.aufdemrand.denizen.objects.properties.Property;
import net.aufdemrand.denizen.objects.properties.PropertyParser;
import net.aufdemrand.denizen.tags.Attribute;
import net.aufdemrand.denizen.tags.core.EscapeTags;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizen.utilities.Utilities;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizen.utilities.entity.Rotation;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.block.Sign;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.block.Skull;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class dLocation extends org.bukkit.Location implements dObject, Notable, Adjustable {

    // This pattern correctly reads both 0.9 and 0.8 notables
    final static Pattern notablePattern =
            Pattern.compile("(\\w+)[;,]((-?\\d+\\.?\\d*,){3,5}.+)",
                    Pattern.CASE_INSENSITIVE);

    /////////////////////
    //   STATIC METHODS
    /////////////////

    public void makeUnique(String id) {
        NotableManager.saveAs(this, id);
    }

    @Note("Locations")
    public String getSaveObject() {
        return (getBlockX() + 0.5)
                + "," + getBlockY()
                + "," + (getBlockZ() + 0.5)
                + "," + getPitch()
                + "," + getYaw()
                + "," + getWorld().getName();
    }

    public static String getSaved(dLocation location) {
        for (dLocation saved : NotableManager.getAllType(dLocation.class)) {
            if (saved.getBlockX() != location.getBlockX()) continue;
            if (saved.getBlockY() != location.getBlockY()) continue;
            if (saved.getBlockZ() != location.getBlockZ()) continue;
            if (!saved.getWorld().getName().equals(location.getWorld().getName())) continue;
            return NotableManager.getSavedId(saved);
        }
        return null;
    }

    public void forget() {
        NotableManager.remove(this);
    }

    /*
     * Called on server startup or /denizen reload locations. Should probably not be called manually.
     */
    public static void _recallLocations() {
        List<String> loclist = DenizenAPI.getCurrentInstance().getSaves().getStringList("dScript.Locations");
        for (String location : loclist) {
            Matcher m = notablePattern.matcher(location);
            if (m.matches()) {
                String id = m.group(1);
                dLocation loc = valueOf(m.group(2));
                NotableManager.saveAs(loc, id);
            }
        }
        DenizenAPI.getCurrentInstance().getSaves().set("dScript.Locations", null);
    }


    //////////////////
    //    OBJECT FETCHER
    ////////////////

    final static Pattern item_by_saved = Pattern.compile("(l@)(.+)");
    /**
     * Gets a Location Object from a string form of id,x,y,z,world
     * or a dScript argument (location:)x,y,z,world. If including an Id,
     * this location will persist and can be recalled at any time.
     *
     * @param string  the string or dScript argument String
     * @return  a Location, or null if incorrectly formatted
     *
     */
    @Fetchable("l")
    public static dLocation valueOf(String string) {
        if (string == null) return null;

        ////////
        // Match @object format for saved dLocations
        Matcher m;

        m = item_by_saved.matcher(string);

        if (m.matches() && NotableManager.isSaved(m.group(2)) && NotableManager.isType(m.group(2), dLocation.class))
            return (dLocation) NotableManager.getSavedObject(m.group(2));

        ////////
        // Match location formats

        // Split values
        String[] split = StringUtils.split(string.startsWith("l@") ? string.substring(2) : string, ',');

        if (split.length == 3)
            // If 4 values, standard dScript location format
            // x,y,z,world
            try {
                return new dLocation(null,
                        Double.valueOf(split[0]),
                        Double.valueOf(split[1]),
                        Double.valueOf(split[2]));
            } catch(Exception e) {
                dB.echoError("valueOf dLocation returning null: " + string + "(internal exception:" + e.getMessage() + ")");
                return null;
            }

        else if (split.length == 4)
            // If 4 values, standard dScript location format
            // x,y,z,world
            try {
                return new dLocation(Bukkit.getWorld(split[3]),
                        Double.valueOf(split[0]),
                        Double.valueOf(split[1]),
                        Double.valueOf(split[2]));
            } catch(Exception e) {
                dB.echoError("valueOf dLocation returning null: " + string + "(internal exception:" + e.getMessage() + ")");
                return null;
            }


        else if (split.length == 6)

            // If 6 values, location with pitch/yaw
            // x,y,z,yaw,pitch,world
            try
            {    return new dLocation(Bukkit.getWorld(split[5]),
                    Double.valueOf(split[0]),
                    Double.valueOf(split[1]),
                    Double.valueOf(split[2]),
                    Float.valueOf(split[3]),
                    Float.valueOf(split[4]));

            } catch(Exception e) {
                dB.echoError("valueOf dLocation returning null: " + string + "(internal exception:" + e.getMessage() + ")");
                return null;
            }

        dB.log("valueOf dLocation returning null: " + string);

        return null;
    }

    final static Pattern location_by_saved = Pattern.compile("(l@)(.+)");

    public static boolean matches(String string) {
        if (string == null || string.length() == 0)
            return false;

        Matcher m = location_by_saved.matcher(string);
        if (m.matches())
            return true;

        String[] data = string.split(",");
        return data.length >= 3 && new Element(data[0]).isDouble()
                && new Element(data[1]).isDouble()
                && new Element(data[2]).isDouble();
    }


    /////////////////////
    //   CONSTRUCTORS
    //////////////////

    /**
     * Turns a Bukkit Location into a Location, which has some helpful methods
     * for working with dScript.
     *
     * @param location the Bukkit Location to reference
     */
    public dLocation(Location location) {
        // Just save the yaw and pitch as they are; don't check if they are
        // higher than 0, because Minecraft yaws are weird and can have
        // negative values
        super(location.getWorld(), location.getX(), location.getY(), location.getZ(),
                location.getYaw(), location.getPitch());
    }

    /**
     * Turns a world and coordinates into a Location, which has some helpful methods
     * for working with dScript.
     *
     * @param world  the world in which the location resides
     * @param x  x-coordinate of the location
     * @param y  y-coordinate of the location
     * @param z  z-coordinate of the location
     *
     */
    public dLocation(World world, double x, double y, double z) {
        super(world, x, y, z);
    }

    public dLocation(World world, double x, double y, double z, float yaw, float pitch) {
        super(world, x, y, z, pitch, yaw);
    }


    /////////////////////
    //   INSTANCE FIELDS/METHODS
    /////////////////

    // A boolean that determines whether this location will identify
    // as a notable or not
    private boolean raw = false;

    private void setRaw(boolean state) {
        this.raw = state;
    }

    @Override
    public void setPitch(float pitch) {
        super.setPitch(pitch);
    }

    // TODO: Why does this and the above exist?
    @Override
    public void setYaw(float yaw) { super.setYaw(yaw); }

    public boolean hasInventory() {
        return getBlock().getState() instanceof InventoryHolder;
    }

    public Inventory getBukkitInventory() {
        return hasInventory() ? ((InventoryHolder) getBlock().getState()).getInventory() : null;
    }

    public dInventory getInventory() {
        return hasInventory() ? new dInventory(getBukkitInventory()) : null;
    }

    public int compare(Location loc1, Location loc2) {
        if (loc1 == null || loc2 == null || loc1.equals(loc2))
            return 0;
        else {
            double dist = distanceSquared(loc1) - distanceSquared(loc2);
            return dist == 0 ? 0: (dist > 0 ? 1: -1);
        }
    }

    @Override
    public int hashCode() {
        return getBlockX() + getBlockY() + getBlockZ();
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!(o instanceof dLocation)) return false;
        dLocation other = (dLocation) o;
        return Math.floor(getX()) == Math.floor(other.getX())
                && Math.floor(getY()) == Math.floor(other.getY())
                && Math.floor(getZ()) == Math.floor(other.getZ());
    }

    String prefix = "Location";

    @Override
    public String getObjectType() {
        return "Location";
    }

    @Override
    public String getPrefix() {
        return prefix;
    }

    @Override
    public dLocation setPrefix(String prefix) {
        this.prefix = prefix;
        return this;
    }

    @Override
    public String debug() {
        return (isUnique() ? "<G>" + prefix + "='<A>" + identify() + "(<Y>" + identifyRaw()+ "<A>)<G>'  "
                : "<G>" + prefix + "='<Y>" + identify() + "<G>'  ");
    }

    @Override
    public boolean isUnique() { return getSaved(this) != null; }

    @Override
    public String identify() {
        if (!raw && isUnique())
            return "l@" + getSaved(this);
        else return identifyRaw();
    }

    @Override
    public String identifySimple() {
        if (isUnique())
            return "l@" + getSaved(this);
        else if (getWorld() == null)
            return "l@" + getBlockX() + "," + getBlockY() + "," + getBlockZ();
        else
            return "l@" + getBlockX() + "," + getBlockY() + "," + getBlockZ()
                + "," + getWorld().getName();
    }

    public String identifyRaw() {
        if (getYaw() != 0.0 || getPitch() != 0.0)
            return "l@" + getX() + "," + getY()
                    + "," + getZ() + "," + getPitch() + "," + getYaw() + (getWorld() != null ? "," + getWorld().getName(): "");
        else
            return "l@" + getX() + "," + getY()
                    + "," + getZ() + (getWorld() != null ? "," + getWorld().getName(): "");
    }

    @Override
    public String toString() {
        return identify();
    }

    @Override
    public String getAttribute(Attribute attribute) {
        if (attribute == null) return null;


        /////////////////////
        //   BLOCK ATTRIBUTES
        /////////////////

        // <--[tag]
        // @attribute <l@location.above>
        // @returns dLocation
        // @description
        // Returns the location one block above this location.
        // -->
        if (attribute.startsWith("above"))
            return new dLocation(this.add(0,1,0))
                    .getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <l@location.below>
        // @returns dLocation
        // @description
        // Returns the location one block below this location.
        // -->
        if (attribute.startsWith("below"))
            return new dLocation(this.add(0,-1,0))
                    .getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <l@location.block>
        // @returns dLocation
        // @description
        // Returns the location of the block this location is on,
        // i.e. returns a location without decimals or direction.
        // -->
        if (attribute.startsWith("block")) {
            return new dLocation(getWorld(), getBlockX(), getBlockY(), getBlockZ())
                    .getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.highest>
        // @returns dLocation
        // @description
        // Returns the location of the highest solid block at the location.
        // -->
        if (attribute.startsWith("highest")) {
            return new dLocation(getWorld().getHighestBlockAt(this).getLocation().add(0, -1, 0))
                    .getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.has_inventory>
        // @returns Element(Boolean)
        // @description
        // Returns whether the block at the location has an inventory.
        // -->
        if (attribute.startsWith("has_inventory")) {
            return new Element(getBlock().getState() instanceof InventoryHolder).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.inventory>
        // @returns dInventory
        // @description
        // Returns the dInventory of the block at the location. If the
        // block is not a container, returns null.
        // -->
        if (attribute.startsWith("inventory")) {
            return Element.handleNull(identify() + ".inventory", getInventory(), "dInventory", attribute.hasAlternative()).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.material>
        // @returns dMaterial
        // @description
        // Returns the material of the block at the location.
        // -->
        if (attribute.startsWith("material"))
            return dMaterial.getMaterialFrom(getBlock().getType(), getBlock().getData()).getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <l@location.switched>
        // @returns Element(Boolean)
        // @description
        // Returns whether the block at the location is considered to be switched on.
        // (For buttons, levers, etc.)
        // To change this, see <@link command Switch>
        // -->
        if (attribute.startsWith("switched"))
            return new Element((getBlock().getData() & 0x8) > 0).getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <l@location.sign_contents>
        // @returns dList
        // @mechanism dLocation.sign_contents
        // @description
        // Returns a list of lines on a sign.
        // -->
        if (attribute.startsWith("sign_contents")) {
            if (getBlock().getState() instanceof Sign) {
                return new dList(Arrays.asList(((Sign) getBlock().getState()).getLines()))
                        .getAttribute(attribute.fulfill(1));
            }
            else return "null";
        }

        // <--[tag]
        // @attribute <l@location.spawner_type>
        // @mechanism dLocation.spawner_type
        // @returns dEntity
        // @description
        // Returns the type of entity spawned by a mob spawner.
        // -->
        if (attribute.startsWith("spawner_type")) {
            if (getBlock().getState() instanceof CreatureSpawner) {
                return new dEntity(((CreatureSpawner) getBlock().getState()).getSpawnedType())
                        .getAttribute(attribute.fulfill(1));
            }
            else return "null";
        }

        // <--[tag]
        // @attribute <l@location.skull_skin>
        // @returns Element
        // @mechanism dLocation.skull_skin
        // @description
        // Returns the skin the skull item is displaying - just the name or UUID as text, not a player object.
        // -->
        if (attribute.startsWith("skull_skin")) {
            if (getBlock().getState() instanceof Skull) {
                return new Element(((Skull) getBlock().getState()).getOwner())
                        .getAttribute(attribute.fulfill(1));
            }
            else return "null";
        }

        // <--[tag]
        // @attribute <l@location.simple.formatted>
        // @returns Element
        // @description
        // Returns the formatted simple version of the dLocation's block coordinates.
        // EG: X 'x', Y 'y', Z 'z', in world 'world'
        // -->
        if (attribute.startsWith("simple.formatted"))
            return new Element("X '" + getBlockX()
                    + "', Y '" + getBlockY()
                    + "', Z '" + getBlockZ()
                    + "', in world '" + getWorld().getName() + "'").getAttribute(attribute.fulfill(2));

        // <--[tag]
        // @attribute <l@location.simple>
        // @returns Element
        // @description
        // Returns a simple version of the dLocation's block coordinates.
        // EG: x,y,z,world
        // -->
        if (attribute.startsWith("simple")) {
            if (getWorld() == null)
                return new Element(getBlockX() + "," + getBlockY() + "," + getBlockZ())
                        .getAttribute(attribute.fulfill(1));
            else
                return new Element(getBlockX() + "," + getBlockY() + "," + getBlockZ()
                        + "," + getWorld().getName()).getAttribute(attribute.fulfill(1));
        }


        /////////////////////
        //   DIRECTION ATTRIBUTES
        /////////////////

        // <--[tag]
        // @attribute <l@location.direction.vector>
        // @returns dLocation
        // @description
        // Returns the location's direction as a one-length vector.
        // -->
        if (attribute.startsWith("direction.vector")) {
            double xzLen = Math.cos((getPitch() % 360) * (Math.PI/180));
            double nx = xzLen * Math.sin(-getYaw() * (Math.PI/180));
            double ny = Math.sin(getPitch() * (Math.PI/180));
            double nz = xzLen * Math.cos(getYaw() * (Math.PI/180));
            return new dLocation(getWorld(), nx, -ny, nz).getAttribute(attribute.fulfill(2));
        }

        // <--[tag]
        // @attribute <l@location.direction[<location>]>
        // @returns Element
        // @description
        // Returns the compass direction between two locations.
        // If no second location is specified, returns the direction of the location.
        // Example returns include "north", "southwest", ...
        // -->
        if (attribute.startsWith("direction")) {
            // Get the cardinal direction from this location to another
            if (attribute.hasContext(1) && dLocation.matches(attribute.getContext(1))) {
                // Subtract this location's vector from the other location's vector,
                // not the other way around
                dLocation target = dLocation.valueOf(attribute.getContext(1));
                attribute = attribute.fulfill(1);
                // <--[tag]
                // @attribute <l@location.direction[<location>].yaw>
                // @returns Element(Decimal)
                // @description
                // Returns the yaw direction between two locations.
                // -->
                if (attribute.startsWith("yaw"))
                    return new Element(Rotation.normalizeYaw(Rotation.getYaw
                            (target.toVector().subtract(this.toVector())
                                    .normalize())))
                            .getAttribute(attribute.fulfill(1));
                else
                    return new Element(Rotation.getCardinal(Rotation.getYaw
                            (target.toVector().subtract(this.toVector())
                                    .normalize())))
                            .getAttribute(attribute);
            }
            // Get a cardinal direction from this location's yaw
            else {
                return new Element(Rotation.getCardinal(getYaw()))
                        .getAttribute(attribute.fulfill(1));
            }
        }

        // <--[tag]
        // @attribute <l@location.facing[<entity>/<location>]>
        // @returns Element(Boolean)
        // @description
        // Returns whether the location's yaw is facing another
        // entity or location.
        // -->
        if (attribute.startsWith("facing")) {
            if (attribute.hasContext(1)) {

                // The default number of degrees if there is no degrees attribute
                int degrees = 45;

                // The attribute to fulfill from
                int attributePos = 1;

                // <--[tag]
                // @attribute <location.facing[<entity>/<location>].degrees[X]>
                // @returns Element(Boolean)
                // @description
                // Returns whether the location's yaw is facing another
                // entity or location, within a specified degree range.
                // -->
                if (attribute.getAttribute(2).startsWith("degrees") &&
                        attribute.hasContext(2) &&
                        aH.matchesInteger(attribute.getContext(2))) {

                    degrees = attribute.getIntContext(2);
                    attributePos++;
                }

                if (dLocation.matches(attribute.getContext(1))) {
                    return new Element(Rotation.isFacingLocation
                            (this, dLocation.valueOf(attribute.getContext(1)), degrees))
                            .getAttribute(attribute.fulfill(attributePos));
                }
                else if (dEntity.matches(attribute.getContext(1))) {
                    return new Element(Rotation.isFacingLocation
                            (this, dEntity.valueOf(attribute.getContext(1))
                                    .getBukkitEntity().getLocation(), degrees))
                            .getAttribute(attribute.fulfill(attributePos));
                }
            }
        }

        // <--[tag]
        // @attribute <l@location.pitch>
        // @returns Element(Decimal)
        // @description
        // Returns the pitch of the object at the location.
        // -->
        if (attribute.startsWith("pitch")) {
            return new Element(getPitch()).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.with_pose[<entity>/<pitch>,<yaw>]>
        // @returns dLocation
        // @description
        // Returns the location with pitch and yaw.
        // -->
        if (attribute.startsWith("with_pose")) {
            String context = attribute.getContext(1);
            Float pitch = 0f;
            Float yaw = 0f;
            if (dEntity.matches(context)) {
                dEntity ent = dEntity.valueOf(context);
                if (ent.isSpawned()) {
                    pitch = ent.getBukkitEntity().getLocation().getPitch();
                    yaw = ent.getBukkitEntity().getLocation().getYaw();
                }
            } else if (context.split(",").length == 2) {
                String[] split = context.split(",");
                pitch = Float.valueOf(split[0]);
                yaw = Float.valueOf(split[1]);
            }
            dLocation loc = dLocation.valueOf(identify());
            loc.setPitch(pitch);
            loc.setYaw(yaw);
            return loc.getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.yaw.simple>
        // @returns Element
        // @description
        // Returns the yaw as 'North', 'South', 'East', or 'West'.
        // -->
        if (attribute.startsWith("yaw.simple")) {
            float yaw = Rotation.normalizeYaw(getYaw());
            if (yaw < 45)
                return new Element("South")
                        .getAttribute(attribute.fulfill(2));
            else if (yaw < 135)
                return new Element("West")
                        .getAttribute(attribute.fulfill(2));
            else if (yaw < 225)
                return new Element("North")
                        .getAttribute(attribute.fulfill(2));
            else if (yaw < 315)
                return new Element("East")
                        .getAttribute(attribute.fulfill(2));
            else
                return new Element("South")
                        .getAttribute(attribute.fulfill(2));
        }

        // <--[tag]
        // @attribute <l@location.yaw.raw>
        // @returns Element(Decimal)
        // @description
        // Returns the raw yaw of the object at the location.
        // -->
        if (attribute.startsWith("yaw.raw")) {
            return new Element(getYaw())
                    .getAttribute(attribute.fulfill(2));
        }

        // <--[tag]
        // @attribute <l@location.yaw>
        // @returns Element(Decimal)
        // @description
        // Returns the normalized yaw of the object at the location.
        // -->
        if (attribute.startsWith("yaw")) {
            return new Element(Rotation.normalizeYaw(getYaw()))
                    .getAttribute(attribute.fulfill(1));
        }


        /////////////////////
        //   ENTITY AND BLOCK LIST ATTRIBUTES
        /////////////////

        if (attribute.startsWith("find") || attribute.startsWith("nearest")) {
            attribute.fulfill(1);

            // <--[tag]
            // @attribute <l@location.find.blocks[<block>|...].within[<#.#>]>
            // @returns dList
            // @description
            // Returns a list of matching blocks within a radius.
            // -->
            if (attribute.startsWith("blocks")
                    && attribute.getAttribute(2).startsWith("within")
                    && attribute.hasContext(2)) {
                ArrayList<dLocation> found = new ArrayList<dLocation>();
                double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
                List<dMaterial> materials = new ArrayList<dMaterial>();
                if (attribute.hasContext(1))
                    materials = dList.valueOf(attribute.getContext(1)).filter(dMaterial.class);
                // Avoid NPE from invalid materials
                if (materials == null) return null;
                int max = Settings.blockTagsMaxBlocks();
                int index = 0;

                // dB.log(materials + " " + radius + " ");
                attribute.fulfill(2);
                Location loc = getBlock().getLocation().add(0.5f, 0.5f, 0.5f);

                fullloop:
                for (double x = -(radius); x <= radius; x++)
                    for (double y = -(radius); y <= radius; y++)
                        for (double z = -(radius); z <= radius; z++) {
                            index++;
                            if (index > max)
                                break fullloop;
                            if (index > max)
                            if (Utilities.checkLocation(loc, getBlock().getLocation().add(x, y, z), radius))
                                if (!materials.isEmpty()) {
                                    for (dMaterial material : materials) {
                                        if (material.hasData() && material.getData() != 0) {
                                            if (material.matchesMaterialData(getBlock()
                                                    .getLocation().add(x, y, z).getBlock().getType().getNewData(getBlock()
                                                            .getLocation().add(x, y, z).getBlock().getData())))
                                                found.add(new dLocation(getBlock().getLocation().add(x + 0.5, y, z + 0.5)));
                                        } else if (material.getMaterial() == getBlock().getLocation().add(x, y, z).getBlock().getType())
                                            found.add(new dLocation(getBlock().getLocation().add(x + 0.5, y, z + 0.5)));
                                    }
                                } else found.add(new dLocation(getBlock().getLocation().add(x + 0.5, y, z + 0.5)));
                        }

                Collections.sort(found, new Comparator<dLocation>() {
                    @Override
                    public int compare(dLocation loc1, dLocation loc2) {
                        return dLocation.this.compare(loc1, loc2);
                    }
                });

                return new dList(found).getAttribute(attribute);
            }

            // <--[tag]
            // @attribute <l@location.find.surface_blocks[<block>|...].within[<#.#>]>
            // @returns dList
            // @description
            // Returns a list of matching surface blocks within a radius.
            // -->
            else if (attribute.startsWith("surface_blocks")
                    && attribute.getAttribute(2).startsWith("within")
                    && attribute.hasContext(2)) {
                ArrayList<dLocation> found = new ArrayList<dLocation>();
                double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
                List<dMaterial> materials = new ArrayList<dMaterial>();
                if (attribute.hasContext(1))
                    materials = dList.valueOf(attribute.getContext(1)).filter(dMaterial.class);
                // Avoid NPE from invalid materials
                if (materials == null) return null;
                int max = Settings.blockTagsMaxBlocks();
                int index = 0;

                attribute.fulfill(2);
                Location loc = getBlock().getLocation().add(0.5f, 0.5f, 0.5f);

                fullloop:
                for (double x = -(radius); x <= radius; x++)
                    for (double y = -(radius); y <= radius; y++)
                        for (double z = -(radius); z <= radius; z++) {
                            index++;
                            if (index > max)
                                break fullloop;
                            if (Utilities.checkLocation(loc, getBlock().getLocation().add(x, y, z), radius)) {
                                Location l = getBlock().getLocation().clone().add(x,y,z);
                                if (!materials.isEmpty()) {
                                    for (dMaterial material : materials) {
                                        if (material.matchesMaterialData(getBlock()
                                                .getLocation().clone().add(x,y,z).getBlock().getType().getNewData(getBlock()
                                                        .getLocation().clone().add(x,y,z).getBlock().getData()))) {
                                            if (l.clone().add(0,1,0).getBlock().getType() == Material.AIR
                                                    && l.clone().add(0,2,0).getBlock().getType() == Material.AIR
                                                    && l.getBlock().getType() != Material.AIR)
                                                found.add(new dLocation(getBlock().getLocation().clone().add(x + 0.5, y, z + 0.5 )));
                                        }
                                    }
                                }
                                else {
                                    if (l.clone().add(0,1,0).getBlock().getType() == Material.AIR
                                            && l.clone().add(0,2,0).getBlock().getType() == Material.AIR
                                            && l.getBlock().getType() != Material.AIR) {
                                        found.add(new dLocation(getBlock().getLocation().clone().add(x + 0.5, y, z + 0.5 )));
                                    }
                                }
                            }
                        }

                Collections.sort(found, new Comparator<dLocation>() {
                    @Override
                    public int compare(dLocation loc1, dLocation loc2) {
                        return dLocation.this.compare(loc1, loc2);
                    }
                });

                return new dList(found).getAttribute(attribute);
            }

            // <--[tag]
            // @attribute <l@location.find.players.within[<#.#>]>
            // @returns dList
            // @description
            // Returns a list of players within a radius.
            // -->
            else if (attribute.startsWith("players")
                    && attribute.getAttribute(2).startsWith("within")
                    && attribute.hasContext(2)) {
                ArrayList<dPlayer> found = new ArrayList<dPlayer>();
                double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
                attribute.fulfill(2);
                for (Player player : Bukkit.getOnlinePlayers())
                    if (!player.isDead() && Utilities.checkLocation(this, player.getLocation(), radius))
                        found.add(new dPlayer(player));

                Collections.sort(found, new Comparator<dPlayer>() {
                    @Override
                    public int compare(dPlayer pl1, dPlayer pl2) {
                        return dLocation.this.compare(pl1.getLocation(), pl2.getLocation());
                    }
                });

                return new dList(found).getAttribute(attribute);
            }

            // <--[tag]
            // @attribute <l@location.find.npcs.within[<#.#>]>
            // @returns dList
            // @description
            // Returns a list of NPCs within a radius.
            // -->
            else if (attribute.startsWith("npcs")
                    && attribute.getAttribute(2).startsWith("within")
                    && attribute.hasContext(2)) {
                ArrayList<dNPC> found = new ArrayList<dNPC>();
                double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
                attribute.fulfill(2);
                for (dNPC npc : DenizenAPI.getSpawnedNPCs())
                    if (Utilities.checkLocation(this.getBlock().getLocation(), npc.getLocation(), radius))
                        found.add(npc);

                Collections.sort(found, new Comparator<dNPC>() {
                    @Override
                    public int compare(dNPC npc1, dNPC npc2) {
                        return dLocation.this.compare(npc1.getLocation(), npc2.getLocation());
                    }
                });

                return new dList(found).getAttribute(attribute);
            }

            // <--[tag]
            // @attribute <l@location.find.entities[<entity>|...].within[<#.#>]>
            // @returns dList
            // @description
            // Returns a list of entities within a radius, with an optional search parameter
            // for the entity type.
            // -->
            else if (attribute.startsWith("entities")
                    && attribute.getAttribute(2).startsWith("within")
                    && attribute.hasContext(2)) {
                dList ent_list = new dList();
                if (attribute.hasContext(1)) {
                    for (String ent : dList.valueOf(attribute.getContext(1))) {
                        if (dEntity.matches(ent))
                            ent_list.add(ent.toUpperCase());
                    }
                }
                ArrayList<dEntity> found = new ArrayList<dEntity>();
                double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
                attribute.fulfill(2);
                for (Entity entity : getWorld().getEntities()) {
                    if (Utilities.checkLocation(this, entity.getLocation(), radius)) {
                        dEntity current = new dEntity(entity);
                        if (!ent_list.isEmpty()) {
                            for (String ent : ent_list) {
                                if ((entity.getType().name().equals(ent) ||
                                        current.identify().equalsIgnoreCase(ent)) && entity.isValid()) {
                                    found.add(current);
                                    break;
                                }
                            }
                        }
                        else
                            found.add(current);
                    }
                }

                Collections.sort(found, new Comparator<dEntity>() {
                    @Override
                    public int compare(dEntity ent1, dEntity ent2) {
                        return dLocation.this.compare(ent1.getLocation(), ent2.getLocation());
                    }
                });

                return new dList(found).getAttribute(attribute);
            }

            // <--[tag]
            // @attribute <l@location.find.living_entities.within[<#.#>]>
            // @returns dList
            // @description
            // Returns a list of living entities within a radius.
            // -->
            else if (attribute.startsWith("living_entities")
                    && attribute.getAttribute(2).startsWith("within")
                    && attribute.hasContext(2)) {
                ArrayList<dEntity> found = new ArrayList<dEntity>();
                double radius = aH.matchesDouble(attribute.getContext(2)) ? attribute.getDoubleContext(2) : 10;
                attribute.fulfill(2);
                for (Entity entity : getWorld().getEntities())
                    if (entity instanceof LivingEntity
                            && Utilities.checkLocation(this, entity.getLocation(), radius))
                        found.add(new dEntity(entity));

                Collections.sort(found, new Comparator<dEntity>() {
                    @Override
                    public int compare(dEntity ent1, dEntity ent2) {
                        return dLocation.this.compare(ent1.getLocation(), ent2.getLocation());
                    }
                });

                return new dList(found).getAttribute(attribute);
            }

            return new Element("null").getAttribute(attribute);
        }


        /////////////////////
        //   IDENTIFICATION ATTRIBUTES
        /////////////////

        // <--[tag]
        // @attribute <l@location.formatted.citizens>
        // @returns Element
        // @description
        // Returns the location formatted for a Citizens command.
        // EG: x.x:y.y:z.z:world
        // -->
        if (attribute.startsWith("formatted.citizens"))
            return new Element(getX() + ":" + getY() + ":" + getZ() + ":" + getWorld().getName()).getAttribute(attribute.fulfill(2));

        // <--[tag]
        // @attribute <l@location.formatted>
        // @returns Element
        // @description
        // Returns the formatted version of the dLocation.
        // EG: 'X 'x.x', Y 'y.y', Z 'z.z', in world 'world'
        // -->
        if (attribute.startsWith("formatted"))
            return new Element("X '" + getX()
                    + "', Y '" + getY()
                    + "', Z '" + getZ()
                    + "', in world '" + getWorld().getName() + "'").getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <l@location.get_chunk>
        // @returns dChunk
        // @description
        // returns the chunk that this location belongs to.
        // -->
        if (attribute.startsWith("get_chunk") ||
                attribute.startsWith("chunk"))
            return new dChunk(this).getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <l@location.raw>
        // @returns dLocation
        // @description
        // returns the raw representation of this location,
        //         ignoring any notables it might match.
        // -->
        if (attribute.startsWith("raw")) {
            dLocation rawLocation = new dLocation(this);
            rawLocation.setRaw(true);
            return rawLocation.getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.world>
        // @returns dWorld
        // @description
        // Returns the world that the location is in.
        // -->
        if (attribute.startsWith("world")) {
            return dWorld.mirrorBukkitWorld(getWorld())
                    .getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.x>
        // @returns Element(Decimal)
        // @description
        // Returns the X coordinate of the location.
        // -->
        if (attribute.startsWith("x")) {
            return new Element(getX()).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.y>
        // @returns Element(Decimal)
        // @description
        // Returns the Y coordinate of the location.
        // -->
        if (attribute.startsWith("y")) {
            return new Element(getY()).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.z>
        // @returns Element(Decimal)
        // @description
        // Returns the Z coordinate of the location.
        // -->
        if (attribute.startsWith("z")) {
            return new Element(getZ()).getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.notable_name>
        // @returns Element
        // @description
        // Gets the name of a Notable dLocation. If the location isn't noted,
        // this is null.
        // -->
        if (attribute.startsWith("notable_name")) {
            return new Element(NotableManager.getSavedId(this)).getAttribute(attribute.fulfill(1));
        }


        /////////////////////
        //   MATHEMATICAL ATTRIBUTES
        /////////////////

        // <--[tag]
        // @attribute <l@location.add[<location>]>
        // @returns dLocation
        // @description
        // Returns the location with the specified coordinates added to it.
        // -->
        if (attribute.startsWith("add")
                && attribute.hasContext(1)) {
            String[] ints = attribute.getContext(1).replace("l@", "").split(",", 4); // TODO: Just dLocation.valueOf?
            if (ints.length >= 3) {
                if ((aH.matchesDouble(ints[0]) || aH.matchesInteger(ints[0]))
                        && (aH.matchesDouble(ints[1]) || aH.matchesInteger(ints[1]))
                        && (aH.matchesDouble(ints[2]) || aH.matchesInteger(ints[2]))) {
                    return new dLocation(this.clone().add(Double.valueOf(ints[0]),
                            Double.valueOf(ints[1]),
                            Double.valueOf(ints[2]))).getAttribute(attribute.fulfill(1));
                }
            }
            else if (dLocation.matches(attribute.getContext(1))) {
                return new dLocation(this.clone().add(dLocation.valueOf(attribute.getContext(1))))
                        .getAttribute(attribute.fulfill(1));
            }
        }

        // <--[tag]
        // @attribute <l@location.sub[<location>]>
        // @returns dLocation
        // @description
        // Returns the location with the specified coordinates subtracted from it.
        // -->
        if (attribute.startsWith("sub")
                && attribute.hasContext(1)) {
            String[] ints = attribute.getContext(1).replace("l@", "").split(",", 4); // TODO: Just dLocation.valueOf?
            if (ints.length == 3 || ints.length == 4) {
                if ((aH.matchesDouble(ints[0]) || aH.matchesInteger(ints[0]))
                        && (aH.matchesDouble(ints[1]) || aH.matchesInteger(ints[1]))
                        && (aH.matchesDouble(ints[2]) || aH.matchesInteger(ints[2]))) {
                    return new dLocation(this.clone().subtract(Double.valueOf(ints[0]),
                            Double.valueOf(ints[1]),
                            Double.valueOf(ints[2]))).getAttribute(attribute.fulfill(1));
                }
            }
            else if (dLocation.matches(attribute.getContext(1))) {
                return new dLocation(this.clone().subtract(dLocation.valueOf(attribute.getContext(1))))
                        .getAttribute(attribute.fulfill(1));
            }
        }

        // <--[tag]
        // @attribute <l@location.mul[<length>]>
        // @returns dLocation
        // @description
        // Returns the location multiplied by the specified length.
        // -->
        if (attribute.startsWith("mul") &&
                attribute.hasContext(1)) {
            return new dLocation(this.clone().multiply(Double.parseDouble(attribute.getContext(1))))
                    .getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.div[<length>]>
        // @returns dLocation
        // @description
        // Returns the location divided by the specified length.
        // -->
        if (attribute.startsWith("div") &&
                attribute.hasContext(1)) {
            return new dLocation(this.clone().multiply(1D / Double.parseDouble(attribute.getContext(1))))
                    .getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.normalize>
        // @returns dLocation
        // @description
        // Returns a 1-length vector in the same direction as this vector location.
        // -->
        if (attribute.startsWith("normalize")) {
            double len = Math.sqrt(Math.pow(getX(), 2) + Math.pow(getY(), 2) + Math.pow(getZ(), 2));
            if (len == 0)
                return this.getAttribute(attribute.fulfill(1));
            else
                return new dLocation(this.clone().multiply(1D / len))
                        .getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.vector_length>
        // @returns Element(Decimal)
        // @description
        // Returns the 3D length of the vector/location.
        // -->
        if (attribute.startsWith("vector_length")) {
            return new Element(Math.sqrt(Math.pow(getX(), 2) + Math.pow(getY(), 2) + Math.pow(getZ(), 2)))
                    .getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.distance_squared[<location>]>
        // @returns Element(Decimal)
        // @description
        // Returns the distance between 2 locations, squared.
        // -->
        if (attribute.startsWith("distance_squared")
                && attribute.hasContext(1)) {
            if (dLocation.matches(attribute.getContext(1))) {
                dLocation toLocation = dLocation.valueOf(attribute.getContext(1));
                return new Element(this.distanceSquared(toLocation))
                        .getAttribute(attribute.fulfill(1));
            }
        }

        // <--[tag]
        // @attribute <l@location.distance[<location>]>
        // @returns Element(Decimal)
        // @description
        // Returns the distance between 2 locations.
        // -->
        if (attribute.startsWith("distance")
                && attribute.hasContext(1)) {
            if (dLocation.matches(attribute.getContext(1))) {
                dLocation toLocation = dLocation.valueOf(attribute.getContext(1));

                // <--[tag]
                // @attribute <l@location.distance[<location>].horizontal>
                // @returns Element(Decimal)
                // @description
                // Returns the horizontal distance between 2 locations.
                // -->
                if (attribute.getAttribute(2).startsWith("horizontal")) {

                    // <--[tag]
                    // @attribute <l@location.distance[<location>].horizontal.multiworld>
                    // @returns Element(Decimal)
                    // @description
                    // Returns the horizontal distance between 2 multiworld locations.
                    // -->
                    if (attribute.getAttribute(3).startsWith("multiworld"))
                        return new Element(Math.sqrt(
                                Math.pow(this.getX() - toLocation.getX(), 2) +
                                        Math.pow(this.getZ() - toLocation.getZ(), 2)))
                                .getAttribute(attribute.fulfill(3));
                    else if (this.getWorld() == toLocation.getWorld())
                        return new Element(Math.sqrt(
                                Math.pow(this.getX() - toLocation.getX(), 2) +
                                        Math.pow(this.getZ() - toLocation.getZ(), 2)))
                                .getAttribute(attribute.fulfill(2));
                }

                // <--[tag]
                // @attribute <l@location.distance[<location>].vertical>
                // @returns Element(Decimal)
                // @description
                // Returns the vertical distance between 2 locations.
                // -->
                else if (attribute.getAttribute(2).startsWith("vertical")) {

                    // <--[tag]
                    // @attribute <l@location.distance[<location>].vertical.multiworld>
                    // @returns Element(Decimal)
                    // @description
                    // Returns the vertical distance between 2 multiworld locations.
                    // -->
                    if (attribute.getAttribute(3).startsWith("multiworld"))
                        return new Element(Math.abs(this.getY() - toLocation.getY()))
                                .getAttribute(attribute.fulfill(3));
                    else if (this.getWorld() == toLocation.getWorld())
                        return new Element(Math.abs(this.getY() - toLocation.getY()))
                                .getAttribute(attribute.fulfill(2));
                }

                else return new Element(this.distance(toLocation))
                            .getAttribute(attribute.fulfill(1));
            }
        }

        // <--[tag]
        // @attribute <l@location.is_within[<cuboid>]>
        // @returns Element(Boolean)
        // @description
        // Returns whether the location is within the cuboid.
        // -->
        if (attribute.startsWith("is_within")
                && attribute.hasContext(1)) {
            dCuboid cuboid = dCuboid.valueOf(attribute.getContext(1));
            if (cuboid != null)
                return new Element(cuboid.isInsideCuboid(this))
                        .getAttribute(attribute.fulfill(1));
        }


        /////////////////////
        //   STATE ATTRIBUTES
        /////////////////

        // <--[tag]
        // @attribute <l@location.biome.formatted>
        // @returns Element
        // @description
        // Returns the formatted biome name at the location.
        // -->
        if (attribute.startsWith("biome.formatted"))
            return new Element(getBlock().getBiome().name().toLowerCase().replace('_', ' '))
                    .getAttribute(attribute.fulfill(2));

        // <--[tag]
        // @attribute <l@location.biome.humidity>
        // @returns Element(Decimal)
        // @description
        // Returns the current humidity at the location.
        // -->
        if (attribute.startsWith("biome.humidity"))
            return new Element(getBlock().getHumidity())
                    .getAttribute(attribute.fulfill(2));

        // <--[tag]
        // @attribute <l@location.biome.temperature>
        // @returns Element(Decimal)
        // @description
        // Returns the current temperature at the location.
        // -->
        if (attribute.startsWith("biome.temperature"))
            return new Element(getBlock().getTemperature())
                    .getAttribute(attribute.fulfill(2));

        // <--[tag]
        // @attribute <l@location.biome>
        // @mechanism dLocation.biome
        // @returns Element
        // @description
        // Returns the biome name at the location.
        // -->
        if (attribute.startsWith("biome"))
            return new Element(getBlock().getBiome().name())
                    .getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <l@location.cuboids>
        // @returns dList(dCuboid)
        // @description
        // Returns a dList of all notable dCuboids that include this location.
        // -->
        if (attribute.startsWith("cuboids")) {
            List<dCuboid> cuboids = dCuboid.getNotableCuboidsContaining(this);
            dList cuboid_list = new dList();
            for (dCuboid cuboid : cuboids) {
                cuboid_list.add(cuboid.identify());
            }
            return cuboid_list.getAttribute(attribute.fulfill(1));
        }

        // <--[tag]
        // @attribute <l@location.is_liquid>
        // @returns Element(Boolean)
        // @description
        // Returns whether block at the location is a liquid.
        // -->
        if (attribute.startsWith("is_liquid"))
            return new Element(getBlock().isLiquid()).getAttribute(attribute.fulfill(1));


        // <--[tag]
        // @attribute <l@location.light.blocks>
        // @returns Element(Number)
        // @description
        // Returns the amount of light from light blocks that is
        // on the location.
        // -->
        if (attribute.startsWith("light.from_blocks") ||
                attribute.startsWith("light.blocks"))
            return new Element(getBlock().getLightFromBlocks())
                    .getAttribute(attribute.fulfill(2));

        // <--[tag]
        // @attribute <l@location.light.sky>
        // @returns Element(Number)
        // @description
        // Returns the amount of light from the sky that is
        // on the location.
        // -->
        if (attribute.startsWith("light.from_sky") ||
                attribute.startsWith("light.sky"))
            return new Element(getBlock().getLightFromSky())
                    .getAttribute(attribute.fulfill(2));

        // <--[tag]
        // @attribute <l@location.light>
        // @returns Element(Number)
        // @description
        // Returns the total amount of light on the location.
        // -->
        if (attribute.startsWith("light"))
            return new Element(getBlock().getLightLevel())
                    .getAttribute(attribute.fulfill(1));

        // <--[tag]
        // @attribute <l@location.power>
        // @returns Element(Number)
        // @description
        // Returns the current redstone power level of a block.
        // -->
        if (attribute.startsWith("power"))
            return new Element(getBlock().getBlockPower())
                    .getAttribute(attribute.fulfill(1));


        // <--[tag]
        // @attribute <l@location.type>
        // @returns Element
        // @description
        // Always returns 'Location' for dLocation objects. All objects fetchable by the Object Fetcher will return the
        // type of object that is fulfilling this attribute.
        // -->
        if (attribute.startsWith("type")) {
            return new Element("Location").getAttribute(attribute.fulfill(1));
        }
        // Iterate through this object's properties' attributes
        for (Property property : PropertyParser.getProperties(this)) {
            String returned = property.getAttribute(attribute);
            if (returned != null) return returned;
        }

        return new Element(identify()).getAttribute(attribute);
    }

    public void applyProperty(Mechanism mechanism) {
        dB.echoError("Cannot apply properties to an location!");
    }

    @Override
    public void adjust(Mechanism mechanism) {

        Element value = mechanism.getValue();

        // <--[mechanism]
        // @object dLocation
        // @name block_type
        // @input dMaterial
        // @description
        // Sets the type of the block.
        // @tags
        // <l@location.material>
        // -->
        if (mechanism.matches("block_type") && mechanism.requireObject(dMaterial.class)) {
            dMaterial mat = value.asType(dMaterial.class);
            byte data = mat.hasData() ? mat.getData(): 0;
            getBlock().setTypeIdAndData(mat.getMaterial().getId(), data, false);
        }

        // <--[mechanism]
        // @object dLocation
        // @name biome
        // @input Element
        // @description
        // Sets the biome of the block.
        // @tags
        // <l@location.biome>
        // -->
        if (mechanism.matches("biome") && mechanism.requireEnum(false, Biome.values())) {
            getBlock().setBiome(Biome.valueOf(value.asString().toUpperCase()));
        }

        // <--[mechanism]
        // @object dLocation
        // @name spawner_type
        // @input dEntity
        // @description
        // Sets the entity that a mob spawner will spawn.
        // @tags
        // <l@location.spawner_type>
        // -->
        if (mechanism.matches("spawner_type") && mechanism.requireObject(dEntity.class)
                && getBlock().getState() instanceof CreatureSpawner) {
            ((CreatureSpawner) getBlock().getState()).setSpawnedType(value.asType(dEntity.class).getEntityType());
        }

        // <--[mechanism]
        // @object dLocation
        // @name sign_contents
        // @input dList
        // @description
        // Sets the contents of a sign block.
        // Note that this takes an escaped list.
        // See <@link language property escaping>.
        // @tags
        // <l@location.sign_contents>
        // -->
        if (mechanism.matches("sign_contents") && getBlock().getState() instanceof Sign) {
            Sign state = (Sign)getBlock().getState();
            for (int i = 0; i < 4; i++)
                state.setLine(i, "");
            dList list = value.asType(dList.class);
            if (list.size() > 4) {
                dB.echoError("Sign can only hold four lines!");
            }
            else {
                for (int i = 0; i < list.size(); i++) {
                    state.setLine(i, EscapeTags.unEscape(list.get(i)));
                }
            }
            state.update();
        }

        // <--[mechanism]
        // @object dLocation
        // @name skull_skin
        // @input Element
        // @description
        // Sets the skin of a skull block.
        // Takes a username.
        // @tags
        // <l@location.skull_skin>
        // -->
        if (mechanism.matches("skull_skin") && getBlock().getState() instanceof Skull) {
            Skull state = ((Skull)getBlock().getState());
            if (!state.setOwner(value.asString()))
                dB.echoError("Failed to set skull_skin!");
            state.update(true);
        }

        if (!mechanism.fulfilled())
            mechanism.reportInvalid();
    }
}
TOP

Related Classes of net.aufdemrand.denizen.objects.dLocation

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.