mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-04 03:56:41 +00:00
Switch to Gradle. Use git log --follow for history.
This converts the project into a multi-module Gradle build. By default, Git does not show history past a rename, so use git log --follow to see further history.
This commit is contained in:
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
public class Countable<T> implements Comparable<Countable<T>> {
|
||||
|
||||
private T id;
|
||||
private int amount;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
*
|
||||
* @param id the ID
|
||||
* @param amount the count of
|
||||
*/
|
||||
public Countable(T id, int amount) {
|
||||
this.id = id;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public T getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setID(T id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the amount.
|
||||
*/
|
||||
public void decrement() {
|
||||
--this.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the amount.
|
||||
*/
|
||||
public void increment() {
|
||||
++this.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Countable<T> other) {
|
||||
if (amount > other.amount) {
|
||||
return 1;
|
||||
} else if (amount == other.amount) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A collection of cardinal, ordinal, and secondary-ordinal directions.
|
||||
*/
|
||||
public enum Direction {
|
||||
|
||||
NORTH(new Vector(0, 0, -1), Flag.CARDINAL),
|
||||
EAST(new Vector(1, 0, 0), Flag.CARDINAL),
|
||||
SOUTH(new Vector(0, 0, 1), Flag.CARDINAL),
|
||||
WEST(new Vector(-1, 0, 0), Flag.CARDINAL),
|
||||
|
||||
UP(new Vector(0, 1, 0), Flag.UPRIGHT),
|
||||
DOWN(new Vector(0, -1, 0), Flag.UPRIGHT),
|
||||
|
||||
NORTHEAST(new Vector(1, 0, -1), Flag.ORDINAL),
|
||||
NORTHWEST(new Vector(-1, 0, -1), Flag.ORDINAL),
|
||||
SOUTHEAST(new Vector(1, 0, 1), Flag.ORDINAL),
|
||||
SOUTHWEST(new Vector(-1, 0, 1), Flag.ORDINAL),
|
||||
|
||||
WEST_NORTHWEST(new Vector(-Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL),
|
||||
WEST_SOUTHWEST(new Vector(-Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL),
|
||||
NORTH_NORTHWEST(new Vector(Math.sin(Math.PI / 8), 0, -Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL),
|
||||
NORTH_NORTHEAST(new Vector(Math.sin(Math.PI / 8), 0, -Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL),
|
||||
EAST_NORTHEAST(new Vector(Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL),
|
||||
EAST_SOUTHEAST(new Vector(Math.cos(Math.PI / 8), 0, Math.sin(Math.PI / 8)), Flag.SECONDARY_ORDINAL),
|
||||
SOUTH_SOUTHEAST(new Vector(Math.sin(Math.PI / 8), 0, Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL),
|
||||
SOUTH_SOUTHWEST(new Vector(Math.sin(Math.PI / 8), 0, Math.cos(Math.PI / 8)), Flag.SECONDARY_ORDINAL);
|
||||
|
||||
private final Vector direction;
|
||||
private final int flags;
|
||||
|
||||
private Direction(Vector vector, int flags) {
|
||||
this.direction = vector.normalize();
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the direction is of a cardinal direction (north, west
|
||||
* east, and south).
|
||||
*
|
||||
* <p>This evaluates as false for directions that have a non-zero
|
||||
* Y-component.</p>
|
||||
*
|
||||
* @return true if cardinal
|
||||
*/
|
||||
public boolean isCardinal() {
|
||||
return (flags & Flag.CARDINAL) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the direction is of an ordinal direction (northwest,
|
||||
* southwest, southeast, northeaast).
|
||||
*
|
||||
* @return true if ordinal
|
||||
*/
|
||||
public boolean isOrdinal() {
|
||||
return (flags & Flag.ORDINAL) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the direction is of a secondary ordinal direction
|
||||
* (north-northwest, north-northeast, south-southwest, etc.).
|
||||
*
|
||||
* @return true if secondary ordinal
|
||||
*/
|
||||
public boolean isSecondaryOrdinal() {
|
||||
return (flags & Flag.SECONDARY_ORDINAL) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether Y component is non-zero.
|
||||
*
|
||||
* @return true if the Y component is non-zero
|
||||
*/
|
||||
public boolean isUpright() {
|
||||
return (flags & Flag.UPRIGHT) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vector.
|
||||
*
|
||||
* @return the vector
|
||||
*/
|
||||
public Vector toVector() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest direction to the given direction vector.
|
||||
*
|
||||
* @param vector the vector
|
||||
* @param flags the only flags that are permitted (use bitwise math)
|
||||
* @return the closest direction, or null if no direction can be returned
|
||||
*/
|
||||
@Nullable
|
||||
public static Direction findClosest(Vector vector, int flags) {
|
||||
if ((flags & Flag.UPRIGHT) == 0) {
|
||||
vector = vector.setY(0);
|
||||
}
|
||||
vector = vector.normalize();
|
||||
|
||||
Direction closest = null;
|
||||
double closestDot = -2;
|
||||
for (Direction direction : values()) {
|
||||
if ((~flags & direction.flags) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double dot = direction.toVector().dot(vector);
|
||||
if (dot >= closestDot) {
|
||||
closest = direction;
|
||||
closestDot = dot;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags to use with {@link #findClosest(Vector, int)}.
|
||||
*/
|
||||
public static final class Flag {
|
||||
public static int CARDINAL = 0x1;
|
||||
public static int ORDINAL = 0x2;
|
||||
public static int SECONDARY_ORDINAL = 0x4;
|
||||
public static int UPRIGHT = 0x8;
|
||||
|
||||
public static int ALL = CARDINAL | ORDINAL | SECONDARY_ORDINAL | UPRIGHT;
|
||||
|
||||
private Flag() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Helper methods for enums.
|
||||
*/
|
||||
public final class Enums {
|
||||
|
||||
private Enums() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the given enum for a value that is equal to the one of the
|
||||
* given values, searching in an ascending manner.
|
||||
*
|
||||
* @param enumType the enum type
|
||||
* @param values the list of values
|
||||
* @param <T> the type of enum
|
||||
* @return the found value or null
|
||||
*/
|
||||
@Nullable
|
||||
public static <T extends Enum<T>> T findByValue(Class<T> enumType, String... values) {
|
||||
checkNotNull(enumType);
|
||||
checkNotNull(values);
|
||||
for (String val : values) {
|
||||
try {
|
||||
return Enum.valueOf(enumType, val);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Indicates that an object can provide various "facets," which are
|
||||
* specific distinct interfaces that can represent a portion of the object.
|
||||
*
|
||||
* <p>For example, an instance of an {@link Entity} may have a facet
|
||||
* for accessing its inventory (if it contains an inventory) or a facet
|
||||
* for accessing its health (if it has health).</p>
|
||||
*
|
||||
* <p>Facets are referred to by their interface or abstract class and
|
||||
* it is dependent on the implementation of the object specifying this
|
||||
* interface to return the most applicable implementation. However, in
|
||||
* many cases, such an implementation may not apply or it has not been
|
||||
* implemented so a request for a facet may return {@code null}.</p>
|
||||
*/
|
||||
public interface Faceted {
|
||||
|
||||
/**
|
||||
* Get the facet corresponding to the given class or interface.
|
||||
*
|
||||
* @param cls the class or interface
|
||||
* @param <T> the type
|
||||
* @return an implementation of the facet or {@code null} if one is unavailable
|
||||
*/
|
||||
@Nullable
|
||||
<T> T getFacet(Class<? extends T> cls);
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.sk89q.util.StringUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class FileDialogUtil {
|
||||
private FileDialogUtil() {
|
||||
}
|
||||
|
||||
public static File showSaveDialog(String[] exts) {
|
||||
JFileChooser dialog = new JFileChooser();
|
||||
|
||||
if (exts != null) {
|
||||
dialog.setFileFilter(new ExtensionFilter(exts));
|
||||
}
|
||||
|
||||
int returnVal = dialog.showSaveDialog(null);
|
||||
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
return dialog.getSelectedFile();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static File showOpenDialog(String[] exts) {
|
||||
JFileChooser dialog = new JFileChooser();
|
||||
|
||||
if (exts != null) {
|
||||
dialog.setFileFilter(new ExtensionFilter(exts));
|
||||
}
|
||||
|
||||
int returnVal = dialog.showOpenDialog(null);
|
||||
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
return dialog.getSelectedFile();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ExtensionFilter extends FileFilter {
|
||||
private Set<String> exts;
|
||||
private String desc;
|
||||
|
||||
private ExtensionFilter(String[] exts) {
|
||||
this.exts = new HashSet<String>(Arrays.asList(exts));
|
||||
|
||||
desc = StringUtil.joinString(exts, ",");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File f) {
|
||||
if (f.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
String path = f.getPath();
|
||||
int index = path.lastIndexOf('.');
|
||||
if (index == -1 || index == path.length() - 1) {
|
||||
return false;
|
||||
} else {
|
||||
return exts.contains(path.indexOf(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents an object that can be identified by a UUID.
|
||||
*/
|
||||
public interface Identifiable {
|
||||
|
||||
/**
|
||||
* Get the UUID for this object.
|
||||
*
|
||||
* @return the UUID
|
||||
*/
|
||||
UUID getUniqueId();
|
||||
|
||||
}
|
@ -0,0 +1,383 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Represents a location in a world with has a direction.
|
||||
*
|
||||
* <p>Like {@code Vectors}, {@code Locations} are immutable and mutator methods
|
||||
* will create a new copy.</p>
|
||||
*
|
||||
* <p>At the moment, but this may change in the future, {@link #hashCode()} and
|
||||
* {@link #equals(Object)} are subject to minor differences caused by
|
||||
* floating point errors.</p>
|
||||
*/
|
||||
public class Location {
|
||||
|
||||
private final Extent extent;
|
||||
private final Vector position;
|
||||
private final float pitch;
|
||||
private final float yaw;
|
||||
|
||||
/**
|
||||
* Create a new instance in the given extent at 0, 0, 0 with a
|
||||
* direction vector of 0, 0, 0.
|
||||
*
|
||||
* @param extent the extent
|
||||
*/
|
||||
public Location(Extent extent) {
|
||||
this(extent, new Vector(), new Vector());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance in the given extent with the given coordinates
|
||||
* with a direction vector of 0, 0, 0.
|
||||
*
|
||||
* @param extent the extent
|
||||
* @param x the X coordinate
|
||||
* @param y the Y coordinate
|
||||
* @param z the Z coordinate
|
||||
*/
|
||||
public Location(Extent extent, double x, double y, double z) {
|
||||
this(extent, new Vector(x, y, z), new Vector());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance in the given extent with the given position
|
||||
* vector and a direction vector of 0, 0, 0.
|
||||
*
|
||||
* @param extent the extent
|
||||
* @param position the position vector
|
||||
*/
|
||||
public Location(Extent extent, Vector position) {
|
||||
this(extent, position, new Vector());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance in the given extent with the given coordinates
|
||||
* and the given direction vector.
|
||||
*
|
||||
* @param extent the extent
|
||||
* @param x the X coordinate
|
||||
* @param y the Y coordinate
|
||||
* @param z the Z coordinate
|
||||
* @param direction the direction vector
|
||||
*/
|
||||
public Location(Extent extent, double x, double y, double z, Vector direction) {
|
||||
this(extent, new Vector(x, y, z), direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance in the given extent with the given coordinates
|
||||
* and the given direction vector.
|
||||
*
|
||||
* @param extent the extent
|
||||
* @param x the X coordinate
|
||||
* @param y the Y coordinate
|
||||
* @param z the Z coordinate
|
||||
* @param yaw the yaw, in degrees
|
||||
* @param pitch the pitch, in degrees
|
||||
*/
|
||||
public Location(Extent extent, double x, double y, double z, float yaw, float pitch) {
|
||||
this(extent, new Vector(x, y, z), yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance in the given extent with the given position vector
|
||||
* and the given direction vector.
|
||||
*
|
||||
* @param extent the extent
|
||||
* @param position the position vector
|
||||
* @param direction the direction vector
|
||||
*/
|
||||
public Location(Extent extent, Vector position, Vector direction) {
|
||||
this(extent, position, direction.toYaw(), direction.toPitch());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance in the given extent with the given position vector
|
||||
* and the given direction vector.
|
||||
*
|
||||
* @param extent the extent
|
||||
* @param position the position vector
|
||||
* @param yaw the yaw, in degrees
|
||||
* @param pitch the pitch, in degrees
|
||||
*/
|
||||
public Location(Extent extent, Vector position, float yaw, float pitch) {
|
||||
checkNotNull(extent);
|
||||
checkNotNull(position);
|
||||
this.extent = extent;
|
||||
this.position = position;
|
||||
this.pitch = pitch;
|
||||
this.yaw = yaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extent.
|
||||
*
|
||||
* @return the extent
|
||||
*/
|
||||
public Extent getExtent() {
|
||||
return extent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of this object with the given extent.
|
||||
*
|
||||
* @param extent the new extent
|
||||
* @return the new instance
|
||||
*/
|
||||
public Location setExtent(Extent extent) {
|
||||
return new Location(extent, position, getDirection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the yaw in degrees.
|
||||
*
|
||||
* @return the yaw in degrees
|
||||
*/
|
||||
public float getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of this object with the given yaw.
|
||||
*
|
||||
* @param yaw the new yaw
|
||||
* @return the new instance
|
||||
*/
|
||||
public Location setYaw(float yaw) {
|
||||
return new Location(extent, position, yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pitch in degrees.
|
||||
*
|
||||
* @return the pitch in degrees
|
||||
*/
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of this object with the given pitch.
|
||||
*
|
||||
* @param pitch the new yaw
|
||||
* @return the new instance
|
||||
*/
|
||||
public Location setPitch(float pitch) {
|
||||
return new Location(extent, position, yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of this object with the given yaw and pitch.
|
||||
*
|
||||
* @param yaw the new yaw
|
||||
* @param pitch the new pitch
|
||||
* @return the new instance
|
||||
*/
|
||||
public Location setDirection(float yaw, float pitch) {
|
||||
return new Location(extent, position, yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direction vector.
|
||||
*
|
||||
* @return the direction vector
|
||||
*/
|
||||
public Vector getDirection() {
|
||||
double yaw = Math.toRadians(this.getYaw());
|
||||
double pitch = Math.toRadians(this.getPitch());
|
||||
double xz = Math.cos(pitch);
|
||||
return new Vector(
|
||||
-xz * Math.sin(yaw),
|
||||
-Math.sin(pitch),
|
||||
xz * Math.cos(yaw));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of this object with the given direction.
|
||||
*
|
||||
* @param direction the new direction
|
||||
* @return the new instance
|
||||
*/
|
||||
public Location setDirection(Vector direction) {
|
||||
return new Location(extent, position, direction.toYaw(), direction.toPitch());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link Vector} form of this location's position.
|
||||
*
|
||||
* @return a vector
|
||||
*/
|
||||
public Vector toVector() {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the X component of the position vector.
|
||||
*
|
||||
* @return the X component
|
||||
*/
|
||||
public double getX() {
|
||||
return position.getX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rounded X component of the position vector.
|
||||
*
|
||||
* @return the rounded X component
|
||||
*/
|
||||
public int getBlockX() {
|
||||
return position.getBlockX();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with the X component of the new object
|
||||
* set to the given value.
|
||||
*
|
||||
* @param x the new value for the X component
|
||||
* @return a new immutable instance
|
||||
*/
|
||||
public Location setX(double x) {
|
||||
return new Location(extent, position.setX(x), yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with the X component of the new object
|
||||
* set to the given value.
|
||||
*
|
||||
* @param x the new value for the X component
|
||||
* @return a new immutable instance
|
||||
*/
|
||||
public Location setX(int x) {
|
||||
return new Location(extent, position.setX(x), yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Y component of the position vector.
|
||||
*
|
||||
* @return the Y component
|
||||
*/
|
||||
public double getY() {
|
||||
return position.getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rounded Y component of the position vector.
|
||||
*
|
||||
* @return the rounded Y component
|
||||
*/
|
||||
public int getBlockY() {
|
||||
return position.getBlockY();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with the Y component of the new object
|
||||
* set to the given value.
|
||||
*
|
||||
* @param y the new value for the Y component
|
||||
* @return a new immutable instance
|
||||
*/
|
||||
public Location setY(double y) {
|
||||
return new Location(extent, position.setY(y), yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with the Y component of the new object
|
||||
* set to the given value.
|
||||
*
|
||||
* @param y the new value for the Y component
|
||||
* @return a new immutable instance
|
||||
*/
|
||||
public Location setY(int y) {
|
||||
return new Location(extent, position.setY(y), yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Z component of the position vector.
|
||||
*
|
||||
* @return the Z component
|
||||
*/
|
||||
public double getZ() {
|
||||
return position.getZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rounded Z component of the position vector.
|
||||
*
|
||||
* @return the rounded Z component
|
||||
*/
|
||||
public int getBlockZ() {
|
||||
return position.getBlockZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with the Z component of the new object
|
||||
* set to the given value.
|
||||
*
|
||||
* @param z the new value for the Y component
|
||||
* @return a new immutable instance
|
||||
*/
|
||||
public Location setZ(double z) {
|
||||
return new Location(extent, position.setZ(z), yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with the Z component of the new object
|
||||
* set to the given value.
|
||||
*
|
||||
* @param z the new value for the Y component
|
||||
* @return a new immutable instance
|
||||
*/
|
||||
public Location setZ(int z) {
|
||||
return new Location(extent, position.setZ(z), yaw, pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Location location = (Location) o;
|
||||
|
||||
if (Double.doubleToLongBits(pitch) != Double.doubleToLongBits(location.pitch)) return false;
|
||||
if (Double.doubleToLongBits(yaw) != Double.doubleToLongBits(location.yaw)) return false;
|
||||
if (!position.equals(location.position)) return false;
|
||||
if (!extent.equals(location.extent)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = extent.hashCode();
|
||||
result = 31 * result + position.hashCode();
|
||||
result = 31 * result + Float.floatToIntBits(this.pitch);
|
||||
result = 31 * result + Float.floatToIntBits(this.yaw);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.sk89q.util.StringUtil;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.world.snapshot.SnapshotRepository;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Simple LocalConfiguration that loads settings using
|
||||
* {@code java.util.Properties}.
|
||||
*/
|
||||
public class PropertiesConfiguration extends LocalConfiguration {
|
||||
|
||||
private static final Logger log = Logger.getLogger(PropertiesConfiguration.class.getCanonicalName());
|
||||
|
||||
protected Properties properties;
|
||||
protected File path;
|
||||
|
||||
/**
|
||||
* Construct the object. The configuration isn't loaded yet.
|
||||
*
|
||||
* @param path the path tot he configuration
|
||||
*/
|
||||
public PropertiesConfiguration(File path) {
|
||||
this.path = path;
|
||||
|
||||
properties = new Properties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = new FileInputStream(path);
|
||||
properties.load(stream);
|
||||
} catch (FileNotFoundException ignored) {
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to read configuration", e);
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profile = getBool("profile", profile);
|
||||
disallowedBlocks = getIntSet("disallowed-blocks", defaultDisallowedBlocks);
|
||||
defaultChangeLimit = getInt("default-max-changed-blocks", defaultChangeLimit);
|
||||
maxChangeLimit = getInt("max-changed-blocks", maxChangeLimit);
|
||||
defaultMaxPolygonalPoints = getInt("default-max-polygon-points", defaultMaxPolygonalPoints);
|
||||
maxPolygonalPoints = getInt("max-polygon-points", maxPolygonalPoints);
|
||||
defaultMaxPolyhedronPoints = getInt("default-max-polyhedron-points", defaultMaxPolyhedronPoints);
|
||||
maxPolyhedronPoints = getInt("max-polyhedron-points", maxPolyhedronPoints);
|
||||
shellSaveType = getString("shell-save-type", shellSaveType);
|
||||
maxRadius = getInt("max-radius", maxRadius);
|
||||
maxSuperPickaxeSize = getInt("max-super-pickaxe-size", maxSuperPickaxeSize);
|
||||
maxBrushRadius = getInt("max-brush-radius", maxBrushRadius);
|
||||
logCommands = getBool("log-commands", logCommands);
|
||||
logFile = getString("log-file", logFile);
|
||||
registerHelp = getBool("register-help", registerHelp);
|
||||
wandItem = getInt("wand-item", wandItem);
|
||||
superPickaxeDrop = getBool("super-pickaxe-drop-items", superPickaxeDrop);
|
||||
superPickaxeManyDrop = getBool("super-pickaxe-many-drop-items", superPickaxeManyDrop);
|
||||
noDoubleSlash = getBool("no-double-slash", noDoubleSlash);
|
||||
useInventory = getBool("use-inventory", useInventory);
|
||||
useInventoryOverride = getBool("use-inventory-override", useInventoryOverride);
|
||||
useInventoryCreativeOverride = getBool("use-inventory-creative-override", useInventoryCreativeOverride);
|
||||
navigationWand = getInt("nav-wand-item", navigationWand);
|
||||
navigationWandMaxDistance = getInt("nav-wand-distance", navigationWandMaxDistance);
|
||||
navigationUseGlass = getBool("nav-use-glass", navigationUseGlass);
|
||||
scriptTimeout = getInt("scripting-timeout", scriptTimeout);
|
||||
saveDir = getString("schematic-save-dir", saveDir);
|
||||
scriptsDir = getString("craftscript-dir", scriptsDir);
|
||||
butcherDefaultRadius = getInt("butcher-default-radius", butcherDefaultRadius);
|
||||
butcherMaxRadius = getInt("butcher-max-radius", butcherMaxRadius);
|
||||
allowSymlinks = getBool("allow-symbolic-links", allowSymlinks);
|
||||
|
||||
LocalSession.MAX_HISTORY_SIZE = Math.max(15, getInt("history-size", 15));
|
||||
|
||||
String snapshotsDir = getString("snapshots-dir", "");
|
||||
if (!snapshotsDir.isEmpty()) {
|
||||
snapshotRepo = new SnapshotRepository(snapshotsDir);
|
||||
}
|
||||
|
||||
OutputStream output = null;
|
||||
path.getParentFile().mkdirs();
|
||||
try {
|
||||
output = new FileOutputStream(path);
|
||||
properties.store(output, "Don't put comments; they get removed");
|
||||
} catch (FileNotFoundException e) {
|
||||
log.log(Level.WARNING, "Failed to write configuration", e);
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to write configuration", e);
|
||||
} finally {
|
||||
if (output != null) {
|
||||
try {
|
||||
output.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string value.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the value
|
||||
*/
|
||||
protected String getString(String key, String def) {
|
||||
if (def == null) {
|
||||
def = "";
|
||||
}
|
||||
String val = properties.getProperty(key);
|
||||
if (val == null) {
|
||||
properties.setProperty(key, def);
|
||||
return def;
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean value.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the value
|
||||
*/
|
||||
protected boolean getBool(String key, boolean def) {
|
||||
String val = properties.getProperty(key);
|
||||
if (val == null) {
|
||||
properties.setProperty(key, def ? "true" : "false");
|
||||
return def;
|
||||
} else {
|
||||
return val.equalsIgnoreCase("true")
|
||||
|| val.equals("1");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an integer value.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the value
|
||||
*/
|
||||
protected int getInt(String key, int def) {
|
||||
String val = properties.getProperty(key);
|
||||
if (val == null) {
|
||||
properties.setProperty(key, String.valueOf(def));
|
||||
return def;
|
||||
} else {
|
||||
try {
|
||||
return Integer.parseInt(val);
|
||||
} catch (NumberFormatException e) {
|
||||
properties.setProperty(key, String.valueOf(def));
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a double value.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the value
|
||||
*/
|
||||
protected double getDouble(String key, double def) {
|
||||
String val = properties.getProperty(key);
|
||||
if (val == null) {
|
||||
properties.setProperty(key, String.valueOf(def));
|
||||
return def;
|
||||
} else {
|
||||
try {
|
||||
return Double.parseDouble(val);
|
||||
} catch (NumberFormatException e) {
|
||||
properties.setProperty(key, String.valueOf(def));
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a double value.
|
||||
*
|
||||
* @param key the key
|
||||
* @param def the default value
|
||||
* @return the value
|
||||
*/
|
||||
protected Set<Integer> getIntSet(String key, int[] def) {
|
||||
String val = properties.getProperty(key);
|
||||
if (val == null) {
|
||||
properties.setProperty(key, StringUtil.joinString(def, ",", 0));
|
||||
Set<Integer> set = new HashSet<Integer>();
|
||||
for (int i : def) {
|
||||
set.add(i);
|
||||
}
|
||||
return set;
|
||||
} else {
|
||||
Set<Integer> set = new HashSet<Integer>();
|
||||
String[] parts = val.split(",");
|
||||
for (String part : parts) {
|
||||
try {
|
||||
int v = Integer.parseInt(part.trim());
|
||||
set.add(v);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.sk89q.worldedit.*;
|
||||
import com.sk89q.worldedit.blocks.BlockID;
|
||||
import com.sk89q.worldedit.blocks.BlockType;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.internal.LocalWorldAdapter;
|
||||
|
||||
/**
|
||||
* This class uses an inefficient method to figure out what block a player
|
||||
* is looking towards.
|
||||
*
|
||||
* <p>Originally written by toi. It was ported to WorldEdit and trimmed down by
|
||||
* sk89q. Thanks to Raphfrk for optimization of toi's original class.</p>
|
||||
*/
|
||||
public class TargetBlock {
|
||||
|
||||
private LocalWorld world;
|
||||
private int maxDistance;
|
||||
private double checkDistance, curDistance;
|
||||
private Vector targetPos = new Vector();
|
||||
private Vector targetPosDouble = new Vector();
|
||||
private Vector prevPos = new Vector();
|
||||
private Vector offset = new Vector();
|
||||
|
||||
/**
|
||||
* Constructor requiring a player, uses default values
|
||||
*
|
||||
* @param player player to work with
|
||||
*/
|
||||
public TargetBlock(LocalPlayer player) {
|
||||
this.world = LocalWorldAdapter.adapt(player.getWorld());
|
||||
this.setValues(player.getPosition(), player.getYaw(), player.getPitch(),
|
||||
300, 1.65, 0.2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor requiring a player, max distance and a checking distance
|
||||
*
|
||||
* @param player LocalPlayer to work with
|
||||
* @param maxDistance how far it checks for blocks
|
||||
* @param checkDistance how often to check for blocks, the smaller the more precise
|
||||
*/
|
||||
public TargetBlock(LocalPlayer player, int maxDistance, double checkDistance) {
|
||||
this((Player) player, maxDistance, checkDistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor requiring a player, max distance and a checking distance
|
||||
*
|
||||
* @param player LocalPlayer to work with
|
||||
* @param maxDistance how far it checks for blocks
|
||||
* @param checkDistance how often to check for blocks, the smaller the more precise
|
||||
*/
|
||||
public TargetBlock(Player player, int maxDistance, double checkDistance) {
|
||||
this.world = LocalWorldAdapter.adapt(player.getWorld());
|
||||
this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), maxDistance, 1.65, checkDistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the values, all constructors uses this function
|
||||
*
|
||||
* @param loc location of the view
|
||||
* @param xRotation the X rotation
|
||||
* @param yRotation the Y rotation
|
||||
* @param maxDistance how far it checks for blocks
|
||||
* @param viewHeight where the view is positioned in y-axis
|
||||
* @param checkDistance how often to check for blocks, the smaller the more precise
|
||||
*/
|
||||
private void setValues(Vector loc, double xRotation, double yRotation,
|
||||
int maxDistance, double viewHeight, double checkDistance) {
|
||||
this.maxDistance = maxDistance;
|
||||
this.checkDistance = checkDistance;
|
||||
this.curDistance = 0;
|
||||
xRotation = (xRotation + 90) % 360;
|
||||
yRotation = yRotation * -1;
|
||||
|
||||
double h = (checkDistance * Math.cos(Math.toRadians(yRotation)));
|
||||
|
||||
offset = new Vector((h * Math.cos(Math.toRadians(xRotation))),
|
||||
(checkDistance * Math.sin(Math.toRadians(yRotation))),
|
||||
(h * Math.sin(Math.toRadians(xRotation))));
|
||||
|
||||
targetPosDouble = loc.add(0, viewHeight, 0);
|
||||
targetPos = targetPosDouble.toBlockPoint();
|
||||
prevPos = targetPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any block at the sight. Returns null if out of range or if no
|
||||
* viable target was found. Will try to return the last valid air block it finds.
|
||||
*
|
||||
* @return Block
|
||||
*/
|
||||
public BlockWorldVector getAnyTargetBlock() {
|
||||
boolean searchForLastBlock = true;
|
||||
BlockWorldVector lastBlock = null;
|
||||
while (getNextBlock() != null) {
|
||||
if (world.getBlockType(getCurrentBlock()) == BlockID.AIR) {
|
||||
if (searchForLastBlock) {
|
||||
lastBlock = getCurrentBlock();
|
||||
if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) {
|
||||
searchForLastBlock = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
BlockWorldVector currentBlock = getCurrentBlock();
|
||||
return (currentBlock != null ? currentBlock : lastBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the block at the sight. Returns null if out of range or if no
|
||||
* viable target was found
|
||||
*
|
||||
* @return Block
|
||||
*/
|
||||
public BlockWorldVector getTargetBlock() {
|
||||
while (getNextBlock() != null && world.getBlockType(getCurrentBlock()) == 0) ;
|
||||
return getCurrentBlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the block at the sight. Returns null if out of range or if no
|
||||
* viable target was found
|
||||
*
|
||||
* @return Block
|
||||
*/
|
||||
public BlockWorldVector getSolidTargetBlock() {
|
||||
while (getNextBlock() != null && BlockType.canPassThrough(world.getBlock(getCurrentBlock()))) ;
|
||||
return getCurrentBlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next block
|
||||
*
|
||||
* @return next block position
|
||||
*/
|
||||
public BlockWorldVector getNextBlock() {
|
||||
prevPos = targetPos;
|
||||
do {
|
||||
curDistance += checkDistance;
|
||||
|
||||
targetPosDouble = offset.add(targetPosDouble.getX(),
|
||||
targetPosDouble.getY(),
|
||||
targetPosDouble.getZ());
|
||||
targetPos = targetPosDouble.toBlockPoint();
|
||||
} while (curDistance <= maxDistance
|
||||
&& targetPos.getBlockX() == prevPos.getBlockX()
|
||||
&& targetPos.getBlockY() == prevPos.getBlockY()
|
||||
&& targetPos.getBlockZ() == prevPos.getBlockZ());
|
||||
|
||||
if (curDistance > maxDistance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new BlockWorldVector(world, targetPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current block along the line of vision
|
||||
*
|
||||
* @return block position
|
||||
*/
|
||||
public BlockWorldVector getCurrentBlock() {
|
||||
if (curDistance > maxDistance) {
|
||||
return null;
|
||||
} else {
|
||||
return new BlockWorldVector(world, targetPos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous block in the aimed path
|
||||
*
|
||||
* @return block position
|
||||
*/
|
||||
public BlockWorldVector getPreviousBlock() {
|
||||
return new BlockWorldVector(world, prevPos);
|
||||
}
|
||||
|
||||
public WorldVectorFace getAnyTargetBlockFace() {
|
||||
getAnyTargetBlock();
|
||||
return WorldVectorFace.getWorldVectorFace(world, getCurrentBlock(), getPreviousBlock());
|
||||
}
|
||||
|
||||
public WorldVectorFace getTargetBlockFace() {
|
||||
getAnyTargetBlock();
|
||||
return WorldVectorFace.getWorldVectorFace(world, getCurrentBlock(), getPreviousBlock());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BlockID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Tree generator.
|
||||
*/
|
||||
public class TreeGenerator {
|
||||
|
||||
public enum TreeType {
|
||||
TREE("Regular tree", "tree", "regular"),
|
||||
BIG_TREE("Big tree", "big", "bigtree"),
|
||||
REDWOOD("Redwood", "redwood", "sequoia", "sequoioideae"),
|
||||
TALL_REDWOOD("Tall redwood", "tallredwood", "tallsequoia", "tallsequoioideae"),
|
||||
BIRCH("Birch", "birch", "white", "whitebark"),
|
||||
PINE("Pine", "pine") {
|
||||
@Override
|
||||
public boolean generate(EditSession editSession, Vector pos) throws MaxChangedBlocksException {
|
||||
makePineTree(editSession, pos);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
RANDOM_REDWOOD("Random redwood", "randredwood", "randomredwood", "anyredwood") {
|
||||
@Override
|
||||
public boolean generate(EditSession editSession, Vector pos) throws MaxChangedBlocksException {
|
||||
TreeType[] choices = new TreeType[] {
|
||||
TreeType.REDWOOD, TreeType.TALL_REDWOOD
|
||||
};
|
||||
return choices[TreeGenerator.RANDOM.nextInt(choices.length)].generate(editSession, pos);
|
||||
}
|
||||
},
|
||||
JUNGLE("Jungle", "jungle"),
|
||||
SMALL_JUNGLE("Small jungle", "shortjungle", "smalljungle"),
|
||||
SHORT_JUNGLE("Short jungle") {
|
||||
@Override
|
||||
public boolean generate(EditSession editSession, Vector pos) throws MaxChangedBlocksException {
|
||||
return SMALL_JUNGLE.generate(editSession, pos);
|
||||
}
|
||||
},
|
||||
JUNGLE_BUSH("Jungle bush", "junglebush", "jungleshrub"),
|
||||
RED_MUSHROOM("Red Mushroom", "redmushroom", "redgiantmushroom"),
|
||||
BROWN_MUSHROOM("Brown Mushroom", "brownmushroom", "browngiantmushroom"),
|
||||
SWAMP("Swamp", "swamp", "swamptree"),
|
||||
ACACIA("Acacia", "acacia"),
|
||||
DARK_OAK("Dark Oak", "darkoak"),
|
||||
MEGA_REDWOOD("Mega Redwood", "megaredwood"),
|
||||
TALL_BIRCH("Tall Birch", "tallbirch"),
|
||||
RANDOM("Random", "rand", "random") {
|
||||
@Override
|
||||
public boolean generate(EditSession editSession, Vector pos) throws MaxChangedBlocksException {
|
||||
TreeType[] choices = new TreeType[] {
|
||||
TreeType.TREE, TreeType.BIG_TREE, TreeType.BIRCH,
|
||||
TreeType.REDWOOD, TreeType.TALL_REDWOOD, TreeType.PINE
|
||||
};
|
||||
return choices[TreeGenerator.RANDOM.nextInt(choices.length)].generate(editSession, pos);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores a map of the names for fast access.
|
||||
*/
|
||||
private static final Map<String, TreeType> lookup = new HashMap<String, TreeType>();
|
||||
|
||||
private final String name;
|
||||
private final String[] lookupKeys;
|
||||
|
||||
static {
|
||||
for (TreeType type : EnumSet.allOf(TreeType.class)) {
|
||||
for (String key : type.lookupKeys) {
|
||||
lookup.put(key, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TreeType(String name, String... lookupKeys) {
|
||||
this.name = name;
|
||||
this.lookupKeys = lookupKeys;
|
||||
}
|
||||
|
||||
public boolean generate(EditSession editSession, Vector pos) throws MaxChangedBlocksException {
|
||||
return editSession.getWorld().generateTree(this, editSession, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user-friendly tree type name.
|
||||
*
|
||||
* @return a name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return type from name. May return null.
|
||||
*
|
||||
* @param name name to search
|
||||
* @return a tree type or null
|
||||
*/
|
||||
@Nullable
|
||||
public static TreeType lookup(String name) {
|
||||
return lookup.get(name.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private TreeType type;
|
||||
|
||||
/**
|
||||
* Construct the tree generator with a tree type.
|
||||
*
|
||||
* @param type the tree type
|
||||
*/
|
||||
@Deprecated
|
||||
public TreeGenerator(TreeType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a tree.
|
||||
*
|
||||
* @param editSession the edit session
|
||||
* @param position the position to generate the tree at
|
||||
* @return true if generation was successful
|
||||
* @throws MaxChangedBlocksException
|
||||
*/
|
||||
public boolean generate(EditSession editSession, Vector position) throws MaxChangedBlocksException {
|
||||
return type.generate(editSession, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a terrible looking pine tree.
|
||||
*
|
||||
* @param basePosition the base position
|
||||
*/
|
||||
private static void makePineTree(EditSession editSession, Vector basePosition)
|
||||
throws MaxChangedBlocksException {
|
||||
int trunkHeight = (int) Math.floor(Math.random() * 2) + 3;
|
||||
int height = (int) Math.floor(Math.random() * 5) + 8;
|
||||
|
||||
BaseBlock logBlock = new BaseBlock(BlockID.LOG);
|
||||
BaseBlock leavesBlock = new BaseBlock(BlockID.LEAVES);
|
||||
|
||||
// Create trunk
|
||||
for (int i = 0; i < trunkHeight; ++i) {
|
||||
if (!editSession.setBlockIfAir(basePosition.add(0, i, 0), logBlock)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Move up
|
||||
basePosition = basePosition.add(0, trunkHeight, 0);
|
||||
|
||||
// Create tree + leaves
|
||||
for (int i = 0; i < height; ++i) {
|
||||
editSession.setBlockIfAir(basePosition.add(0, i, 0), logBlock);
|
||||
|
||||
// Less leaves at these levels
|
||||
double chance = ((i == 0 || i == height - 1) ? 0.6 : 1);
|
||||
|
||||
// Inner leaves
|
||||
editSession.setChanceBlockIfAir(basePosition.add(-1, i, 0), leavesBlock, chance);
|
||||
editSession.setChanceBlockIfAir(basePosition.add(1, i, 0), leavesBlock, chance);
|
||||
editSession.setChanceBlockIfAir(basePosition.add(0, i, -1), leavesBlock, chance);
|
||||
editSession.setChanceBlockIfAir(basePosition.add(0, i, 1), leavesBlock, chance);
|
||||
editSession.setChanceBlockIfAir(basePosition.add(1, i, 1), leavesBlock, chance);
|
||||
editSession.setChanceBlockIfAir(basePosition.add(-1, i, 1), leavesBlock, chance);
|
||||
editSession.setChanceBlockIfAir(basePosition.add(1, i, -1), leavesBlock, chance);
|
||||
editSession.setChanceBlockIfAir(basePosition.add(-1, i, -1), leavesBlock, chance);
|
||||
|
||||
if (!(i == 0 || i == height - 1)) {
|
||||
for (int j = -2; j <= 2; ++j) {
|
||||
editSession.setChanceBlockIfAir(basePosition.add(-2, i, j), leavesBlock, 0.6);
|
||||
}
|
||||
for (int j = -2; j <= 2; ++j) {
|
||||
editSession.setChanceBlockIfAir(basePosition.add(2, i, j), leavesBlock, 0.6);
|
||||
}
|
||||
for (int j = -2; j <= 2; ++j) {
|
||||
editSession.setChanceBlockIfAir(basePosition.add(j, i, -2), leavesBlock, 0.6);
|
||||
}
|
||||
for (int j = -2; j <= 2; ++j) {
|
||||
editSession.setChanceBlockIfAir(basePosition.add(j, i, 2), leavesBlock, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editSession.setBlockIfAir(basePosition.add(0, height, 0), leavesBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a tree type. May return null if a tree type by that
|
||||
* name is not found.
|
||||
*
|
||||
* @param type the tree type
|
||||
* @return a tree type or null
|
||||
*/
|
||||
@Nullable
|
||||
public static TreeType lookup(String type) {
|
||||
return TreeType.lookup(type);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Returns the best choice given a weighting function and a target weight.
|
||||
*
|
||||
* <p>A function must be supplied that returns a numeric score for each
|
||||
* choice. The function can return null to mean that the choice should
|
||||
* not be considered.</p>
|
||||
*
|
||||
* @param <T> the type of choice
|
||||
*/
|
||||
public class WeightedChoice<T> {
|
||||
|
||||
private final Function<T, ? extends Number> function;
|
||||
private double target;
|
||||
private double best;
|
||||
private T current;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param function a function that assigns a score for each choice
|
||||
* @param target the target score that the best choice should be closest to
|
||||
*/
|
||||
public WeightedChoice(Function<T, ? extends Number> function, double target) {
|
||||
checkNotNull(function);
|
||||
this.function = function;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider the given object.
|
||||
*
|
||||
* @param object the choice
|
||||
*/
|
||||
public void consider(T object) {
|
||||
checkNotNull(object);
|
||||
Number value = checkNotNull(function.apply(object));
|
||||
if (value != null) {
|
||||
double distance = Math.abs(target - value.doubleValue());
|
||||
if (current == null || distance <= best) {
|
||||
best = distance;
|
||||
current = object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the best choice.
|
||||
*
|
||||
* @return the best choice
|
||||
*/
|
||||
public Optional<Choice<T>> getChoice() {
|
||||
if (current != null) {
|
||||
return Optional.of(new Choice<T>(current, best));
|
||||
} else {
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tuple of choice and score.
|
||||
*
|
||||
* @param <T> the choice type
|
||||
*/
|
||||
public static class Choice<T> {
|
||||
private final T object;
|
||||
private final double value;
|
||||
|
||||
private Choice(T object, double value) {
|
||||
this.object = object;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the chosen value.
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
public T getValue() {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the score.
|
||||
*
|
||||
* @return the score
|
||||
*/
|
||||
public double getScore() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
import com.sk89q.util.yaml.YAMLProcessor;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.session.SessionManager;
|
||||
import com.sk89q.worldedit.world.snapshot.SnapshotRepository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A less simple implementation of {@link LocalConfiguration}
|
||||
* using YAML configuration files.
|
||||
*/
|
||||
public class YAMLConfiguration extends LocalConfiguration {
|
||||
|
||||
protected final YAMLProcessor config;
|
||||
protected final Logger logger;
|
||||
|
||||
public YAMLConfiguration(YAMLProcessor config, Logger logger) {
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
try {
|
||||
config.load();
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.WARNING, "Error loading WorldEdit configuration", e);
|
||||
}
|
||||
|
||||
profile = config.getBoolean("debug", profile);
|
||||
wandItem = config.getInt("wand-item", wandItem);
|
||||
|
||||
defaultChangeLimit = Math.max(-1, config.getInt(
|
||||
"limits.max-blocks-changed.default", defaultChangeLimit));
|
||||
maxChangeLimit = Math.max(-1,
|
||||
config.getInt("limits.max-blocks-changed.maximum", maxChangeLimit));
|
||||
|
||||
defaultMaxPolygonalPoints = Math.max(-1,
|
||||
config.getInt("limits.max-polygonal-points.default", defaultMaxPolygonalPoints));
|
||||
maxPolygonalPoints = Math.max(-1,
|
||||
config.getInt("limits.max-polygonal-points.maximum", maxPolygonalPoints));
|
||||
|
||||
defaultMaxPolyhedronPoints = Math.max(-1, config.getInt("limits.max-polyhedron-points.default", defaultMaxPolyhedronPoints));
|
||||
maxPolyhedronPoints = Math.max(-1, config.getInt("limits.max-polyhedron-points.maximum", maxPolyhedronPoints));
|
||||
|
||||
maxRadius = Math.max(-1, config.getInt("limits.max-radius", maxRadius));
|
||||
maxBrushRadius = config.getInt("limits.max-brush-radius", maxBrushRadius);
|
||||
maxSuperPickaxeSize = Math.max(1, config.getInt(
|
||||
"limits.max-super-pickaxe-size", maxSuperPickaxeSize));
|
||||
|
||||
butcherDefaultRadius = Math.max(-1, config.getInt("limits.butcher-radius.default", butcherDefaultRadius));
|
||||
butcherMaxRadius = Math.max(-1, config.getInt("limits.butcher-radius.maximum", butcherMaxRadius));
|
||||
|
||||
disallowedBlocks = new HashSet<Integer>(config.getIntList("limits.disallowed-blocks", null));
|
||||
allowedDataCycleBlocks = new HashSet<Integer>(config.getIntList("limits.allowed-data-cycle-blocks", null));
|
||||
|
||||
registerHelp = config.getBoolean("register-help", true);
|
||||
logCommands = config.getBoolean("logging.log-commands", logCommands);
|
||||
logFile = config.getString("logging.file", logFile);
|
||||
|
||||
superPickaxeDrop = config.getBoolean("super-pickaxe.drop-items",
|
||||
superPickaxeDrop);
|
||||
superPickaxeManyDrop = config.getBoolean(
|
||||
"super-pickaxe.many-drop-items", superPickaxeManyDrop);
|
||||
|
||||
noDoubleSlash = config.getBoolean("no-double-slash", noDoubleSlash);
|
||||
|
||||
useInventory = config.getBoolean("use-inventory.enable", useInventory);
|
||||
useInventoryOverride = config.getBoolean("use-inventory.allow-override",
|
||||
useInventoryOverride);
|
||||
useInventoryCreativeOverride = config.getBoolean("use-inventory.creative-mode-overrides",
|
||||
useInventoryCreativeOverride);
|
||||
|
||||
navigationWand = config.getInt("navigation-wand.item", navigationWand);
|
||||
navigationWandMaxDistance = config.getInt("navigation-wand.max-distance", navigationWandMaxDistance);
|
||||
navigationUseGlass = config.getBoolean("navigation.use-glass", navigationUseGlass);
|
||||
|
||||
scriptTimeout = config.getInt("scripting.timeout", scriptTimeout);
|
||||
scriptsDir = config.getString("scripting.dir", scriptsDir);
|
||||
|
||||
saveDir = config.getString("saving.dir", saveDir);
|
||||
|
||||
allowSymlinks = config.getBoolean("files.allow-symbolic-links", false);
|
||||
LocalSession.MAX_HISTORY_SIZE = Math.max(0, config.getInt("history.size", 15));
|
||||
SessionManager.EXPIRATION_GRACE = config.getInt("history.expiration", 10) * 60 * 1000;
|
||||
|
||||
showHelpInfo = config.getBoolean("show-help-on-first-use", true);
|
||||
|
||||
String snapshotsDir = config.getString("snapshots.directory", "");
|
||||
if (!snapshotsDir.isEmpty()) {
|
||||
snapshotRepo = new SnapshotRepository(snapshotsDir);
|
||||
}
|
||||
|
||||
String type = config.getString("shell-save-type", "").trim();
|
||||
shellSaveType = type.equals("") ? null : type;
|
||||
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.auth;
|
||||
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
|
||||
/**
|
||||
* Raised when authorization is not granted.
|
||||
*/
|
||||
public class AuthorizationException extends WorldEditException {
|
||||
|
||||
public AuthorizationException() {
|
||||
}
|
||||
|
||||
public AuthorizationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AuthorizationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AuthorizationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.auth;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
|
||||
/**
|
||||
* Tests whether permission is granted.
|
||||
*/
|
||||
public interface Authorizer {
|
||||
|
||||
/**
|
||||
* Tests whether permission is granted for the given context.
|
||||
*
|
||||
* @param locals locals
|
||||
* @param permission the permission string
|
||||
* @return true if permitted
|
||||
*/
|
||||
boolean testPermission(CommandLocals locals, String permission);
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.auth;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
|
||||
/**
|
||||
* An implementation of {@link Authorizer} that always returns false for
|
||||
* tests of permissions.
|
||||
*/
|
||||
public class NullAuthorizer implements Authorizer {
|
||||
|
||||
@Override
|
||||
public boolean testPermission(CommandLocals locals, String permission) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.auth;
|
||||
|
||||
/**
|
||||
* A subject has authorization attached to it.
|
||||
*/
|
||||
public interface Subject {
|
||||
|
||||
/**
|
||||
* Get a list of groups that this subject is a part of.
|
||||
*
|
||||
* @return an array containing a group name per entry
|
||||
*/
|
||||
String[] getGroups();
|
||||
|
||||
/**
|
||||
* Check whether this subject has been granted the given permission
|
||||
* and throw an exception on error.
|
||||
*
|
||||
* @param permission the permission
|
||||
* @throws AuthorizationException thrown if not permitted
|
||||
*/
|
||||
void checkPermission(String permission) throws AuthorizationException;
|
||||
|
||||
/**
|
||||
* Return whether this subject has the given permission.
|
||||
*
|
||||
* @param permission the permission
|
||||
* @return true if permission is granted
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.collection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Double array lists to work like a Map, but not really.
|
||||
*
|
||||
* <p>The usefulness of this class is highly questionable.</p>
|
||||
*/
|
||||
public class DoubleArrayList<A, B> implements Iterable<Map.Entry<A, B>> {
|
||||
|
||||
private List<A> listA = new ArrayList<A>();
|
||||
private List<B> listB = new ArrayList<B>();
|
||||
private boolean isReversed = false;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
*
|
||||
* @param isReversed true if the list should be reversed
|
||||
*/
|
||||
public DoubleArrayList(boolean isReversed) {
|
||||
this.isReversed = isReversed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item.
|
||||
*
|
||||
* @param a the first item
|
||||
* @param b the second item
|
||||
*/
|
||||
public void put(A a, B b) {
|
||||
listA.add(a);
|
||||
listB.add(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size.
|
||||
*
|
||||
* @return count of objects
|
||||
*/
|
||||
public int size() {
|
||||
return listA.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the list.
|
||||
*/
|
||||
public void clear() {
|
||||
listA.clear();
|
||||
listB.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry set.
|
||||
*
|
||||
* @return entry set
|
||||
*/
|
||||
public Iterator<Map.Entry<A, B>> iterator(boolean reversed) {
|
||||
if (reversed) {
|
||||
return new ReverseEntryIterator<Map.Entry<A, B>>(
|
||||
listA.listIterator(listA.size()),
|
||||
listB.listIterator(listB.size()));
|
||||
} else {
|
||||
return new ForwardEntryIterator<Map.Entry<A, B>>(
|
||||
listA.iterator(),
|
||||
listB.iterator());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<A, B>> iterator() {
|
||||
return iterator(isReversed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry iterator.
|
||||
*/
|
||||
public class ForwardEntryIterator<T extends Map.Entry<A, B>>
|
||||
implements Iterator<Map.Entry<A, B>> {
|
||||
|
||||
private Iterator<A> keyIterator;
|
||||
private Iterator<B> valueIterator;
|
||||
|
||||
public ForwardEntryIterator(Iterator<A> keyIterator, Iterator<B> valueIterator) {
|
||||
this.keyIterator = keyIterator;
|
||||
this.valueIterator = valueIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return keyIterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<A, B> next() throws NoSuchElementException {
|
||||
return new Entry<A, B>(keyIterator.next(), valueIterator.next());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry iterator.
|
||||
*/
|
||||
public class ReverseEntryIterator<T extends Map.Entry<A, B>>
|
||||
implements Iterator<Map.Entry<A, B>> {
|
||||
|
||||
private ListIterator<A> keyIterator;
|
||||
private ListIterator<B> valueIterator;
|
||||
|
||||
public ReverseEntryIterator(ListIterator<A> keyIterator, ListIterator<B> valueIterator) {
|
||||
this.keyIterator = keyIterator;
|
||||
this.valueIterator = valueIterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return keyIterator.hasPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<A, B> next() throws NoSuchElementException {
|
||||
return new Entry<A, B>(keyIterator.previous(), valueIterator.previous());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to masquerade as Map.Entry.
|
||||
*/
|
||||
public class Entry<C, D> implements Map.Entry<A, B> {
|
||||
private A key;
|
||||
private B value;
|
||||
|
||||
private Entry(A key, B value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public A getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public B getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public B setValue(B value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.collection;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A fast iterator for lists that uses an internal index integer
|
||||
* and caches the size of the list. The size of the list cannot change
|
||||
* during iteration and {@link Iterator#remove()} is not supported.
|
||||
*
|
||||
* <p>The iterator in Java, at least in older Java versions, is very slow,
|
||||
* causing a significant amount of time in operations in WorldEdit
|
||||
* being spent on {@link Iterator#hasNext()}. In contrast, the iterator
|
||||
* implemented by this class is very quick, as long as
|
||||
* {@link List#get(int)} is fast.</p>
|
||||
*
|
||||
* @param <E> the element
|
||||
*/
|
||||
public class FastListIterator<E> implements Iterator<E> {
|
||||
|
||||
private final List<E> list;
|
||||
private int index;
|
||||
private final int size;
|
||||
private final int increment;
|
||||
|
||||
/**
|
||||
* Create a new fast iterator.
|
||||
*
|
||||
* @param list the list
|
||||
* @param index the index to start from
|
||||
* @param size the size of the list
|
||||
* @param increment the increment amount (i.e. 1 or -1)
|
||||
*/
|
||||
private FastListIterator(List<E> list, int index, int size, int increment) {
|
||||
checkNotNull(list);
|
||||
checkArgument(size >= 0, "size >= 0 required");
|
||||
checkArgument(index >= 0, "index >= 0 required");
|
||||
this.list = list;
|
||||
this.index = index;
|
||||
this.size = size;
|
||||
this.increment = increment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index >= 0 && index < size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
if (hasNext()) {
|
||||
E entry = list.get(index);
|
||||
index += increment;
|
||||
return entry;
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new forward iterator for the given list.
|
||||
*
|
||||
* @param list the list
|
||||
* @param <E> the element
|
||||
* @return an iterator
|
||||
*/
|
||||
public static <E> Iterator<E> forwardIterator(List<E> list) {
|
||||
return new FastListIterator<E>(list, 0, list.size(), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new reverse iterator for the given list.
|
||||
*
|
||||
* @param list the list
|
||||
* @param <E> the element
|
||||
* @return an iterator
|
||||
*/
|
||||
public static <E> Iterator<E> reverseIterator(List<E> list) {
|
||||
if (!list.isEmpty()) {
|
||||
return new FastListIterator<E>(list, list.size() - 1, list.size(), -1);
|
||||
} else {
|
||||
return new FastListIterator<E>(list, 0, 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.collection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An {@link ArrayList} that takes {@link Map.Entry}-like tuples. This class
|
||||
* exists for legacy reasons.
|
||||
*
|
||||
* @param <A> the first type in the tuple
|
||||
* @param <B> the second type in the tuple
|
||||
*/
|
||||
public class TupleArrayList<A, B> extends ArrayList<Map.Entry<A, B>> {
|
||||
|
||||
/**
|
||||
* Add an item to the list.
|
||||
*
|
||||
* @param a the 'key'
|
||||
* @param b the 'value'
|
||||
*/
|
||||
public void put(A a, B b) {
|
||||
add(new Tuple<A, B>(a, b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an entry iterator that traverses in the reverse direction.
|
||||
*
|
||||
* @param reverse true to return the reverse iterator
|
||||
* @return an entry iterator
|
||||
*/
|
||||
public Iterator<Map.Entry<A, B>> iterator(boolean reverse) {
|
||||
return reverse ? reverseIterator() : iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<A, B>> iterator() {
|
||||
return FastListIterator.forwardIterator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an entry iterator that traverses in the reverse direction.
|
||||
*
|
||||
* @return an entry iterator
|
||||
*/
|
||||
public Iterator<Map.Entry<A, B>> reverseIterator() {
|
||||
return FastListIterator.reverseIterator(this);
|
||||
}
|
||||
|
||||
private static class Tuple<A, B> implements Map.Entry<A, B> {
|
||||
private A key;
|
||||
private B value;
|
||||
|
||||
private Tuple(A key, B value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public A getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public B getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public B setValue(B value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
|
||||
/**
|
||||
* A command that can be executed.
|
||||
*/
|
||||
public interface CommandCallable extends CommandCompleter {
|
||||
|
||||
/**
|
||||
* Execute the correct command based on the input.
|
||||
*
|
||||
* <p>The implementing class must perform the necessary permission
|
||||
* checks.</p>
|
||||
*
|
||||
* @param arguments the arguments
|
||||
* @param locals the locals
|
||||
* @param parentCommands a list of parent commands, with the first most entry being the top-level command
|
||||
* @return the called command, or null if there was no command found
|
||||
* @throws CommandException thrown on a command error
|
||||
*/
|
||||
boolean call(String arguments, CommandLocals locals, String[] parentCommands) throws CommandException;
|
||||
|
||||
/**
|
||||
* Get an object describing this command.
|
||||
*
|
||||
* @return the command description
|
||||
*/
|
||||
Description getDescription();
|
||||
|
||||
/**
|
||||
* Test whether this command can be executed with the given context.
|
||||
*
|
||||
* @param locals the locals
|
||||
* @return true if execution is permitted
|
||||
*/
|
||||
boolean testPermission(CommandLocals locals);
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides a method that can provide tab completion for commands.
|
||||
*/
|
||||
public interface CommandCompleter {
|
||||
|
||||
/**
|
||||
* Get a list of suggestions based on input.
|
||||
*
|
||||
* @param arguments the arguments entered up to this point
|
||||
* @param locals the locals
|
||||
* @return a list of suggestions
|
||||
* @throws CommandException thrown if there was a parsing error
|
||||
*/
|
||||
List<String> getSuggestions(String arguments, CommandLocals locals) throws CommandException;
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
/**
|
||||
* Provides information about a mapping between a command and its aliases.
|
||||
*/
|
||||
public interface CommandMapping {
|
||||
|
||||
/**
|
||||
* Get the primary alias.
|
||||
*
|
||||
* @return the primary alias
|
||||
*/
|
||||
String getPrimaryAlias();
|
||||
|
||||
/**
|
||||
* Get a list of all aliases.
|
||||
*
|
||||
* @return aliases
|
||||
*/
|
||||
String[] getAllAliases();
|
||||
|
||||
/**
|
||||
* Get the callable
|
||||
*
|
||||
* @return the callable
|
||||
*/
|
||||
CommandCallable getCallable();
|
||||
|
||||
/**
|
||||
* Get the {@link Description} form the callable.
|
||||
*
|
||||
* @return the description
|
||||
*/
|
||||
Description getDescription();
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A description of a command.
|
||||
*/
|
||||
public interface Description {
|
||||
|
||||
/**
|
||||
* Get the list of parameters for this command.
|
||||
*
|
||||
* @return a list of parameters
|
||||
*/
|
||||
List<Parameter> getParameters();
|
||||
|
||||
/**
|
||||
* Get a short one-line description of this command.
|
||||
*
|
||||
* @return a description, or null if no description is available
|
||||
*/
|
||||
String getShortDescription();
|
||||
|
||||
/**
|
||||
* Get a longer help text about this command.
|
||||
*
|
||||
* @return a help text, or null if no help is available
|
||||
*/
|
||||
String getHelp();
|
||||
|
||||
/**
|
||||
* Get the usage string of this command.
|
||||
*
|
||||
* <p>A usage string may look like
|
||||
* {@code [-w <world>] <var1> <var2>}.</p>
|
||||
*
|
||||
* @return a usage string
|
||||
*/
|
||||
String getUsage();
|
||||
|
||||
/**
|
||||
* Get a list of permissions that the player may have to have permission.
|
||||
*
|
||||
* <p>Permission data may or may not be available. This is only useful as a
|
||||
* potential hint.</p>
|
||||
*
|
||||
* @return the list of permissions
|
||||
*/
|
||||
List<String> getPermissions();
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Executes a command based on user input.
|
||||
*/
|
||||
public interface Dispatcher extends CommandCallable {
|
||||
|
||||
/**
|
||||
* Register a command with this dispatcher.
|
||||
*
|
||||
* @param callable the command executor
|
||||
* @param alias a list of aliases, where the first alias is the primary name
|
||||
*/
|
||||
void registerCommand(CommandCallable callable, String... alias);
|
||||
|
||||
/**
|
||||
* Get a list of commands. Each command, regardless of how many aliases
|
||||
* it may have, will only appear once in the returned set.
|
||||
*
|
||||
* <p>The returned collection cannot be modified.</p>
|
||||
*
|
||||
* @return a list of registrations
|
||||
*/
|
||||
Set<CommandMapping> getCommands();
|
||||
|
||||
/**
|
||||
* Get a list of primary aliases.
|
||||
*
|
||||
* <p>The returned collection cannot be modified.</p>
|
||||
*
|
||||
* @return a list of aliases
|
||||
*/
|
||||
Collection<String> getPrimaryAliases();
|
||||
|
||||
/**
|
||||
* Get a list of all the command aliases, which includes the primary alias.
|
||||
*
|
||||
* <p>A command may have more than one alias assigned to it. The returned
|
||||
* collection cannot be modified.</p>
|
||||
*
|
||||
* @return a list of aliases
|
||||
*/
|
||||
Collection<String> getAliases();
|
||||
|
||||
/**
|
||||
* Get the {@link CommandCallable} associated with an alias. Returns
|
||||
* null if no command is named by the given alias.
|
||||
*
|
||||
* @param alias the alias
|
||||
* @return the command mapping (null if not found)
|
||||
*/
|
||||
@Nullable CommandMapping get(String alias);
|
||||
|
||||
/**
|
||||
* Returns whether the dispatcher contains a registered command for the given alias.
|
||||
*
|
||||
* @param alias the alias
|
||||
* @return true if a registered command exists
|
||||
*/
|
||||
boolean contains(String alias);
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Thrown when a command is not used properly.
|
||||
*
|
||||
* <p>When handling this exception, print the error message if it is not null.
|
||||
* Print a one line help instruction unless {@link #isFullHelpSuggested()}
|
||||
* is true, which, in that case, the full help of the command should be
|
||||
* shown.</p>
|
||||
*
|
||||
* <p>If no error message is set and full help is not to be shown, then a generic
|
||||
* "you used this command incorrectly" message should be shown.</p>
|
||||
*/
|
||||
public class InvalidUsageException extends CommandException {
|
||||
|
||||
private final CommandCallable command;
|
||||
private final boolean fullHelpSuggested;
|
||||
|
||||
/**
|
||||
* Create a new instance with no error message and with no suggestion
|
||||
* that full and complete help for the command should be shown. This will
|
||||
* result in a generic error message.
|
||||
*
|
||||
* @param command the command
|
||||
*/
|
||||
public InvalidUsageException(CommandCallable command) {
|
||||
this(null, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a message and with no suggestion
|
||||
* that full and complete help for the command should be shown.
|
||||
*
|
||||
* @param message the message
|
||||
* @param command the command
|
||||
*/
|
||||
public InvalidUsageException(@Nullable String message, CommandCallable command) {
|
||||
this(message, command, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with a message.
|
||||
*
|
||||
* @param message the message
|
||||
* @param command the command
|
||||
* @param fullHelpSuggested true if the full help for the command should be shown
|
||||
*/
|
||||
public InvalidUsageException(@Nullable String message, CommandCallable command, boolean fullHelpSuggested) {
|
||||
super(message);
|
||||
checkNotNull(command);
|
||||
this.command = command;
|
||||
this.fullHelpSuggested = fullHelpSuggested;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command.
|
||||
*
|
||||
* @return the command
|
||||
*/
|
||||
public CommandCallable getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a simple usage string.
|
||||
*
|
||||
* @param prefix the command shebang (such as "/") -- may be blank
|
||||
* @return a usage string
|
||||
*/
|
||||
public String getSimpleUsageString(String prefix) {
|
||||
return getCommandUsed(prefix, command.getDescription().getUsage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the full usage of the command should be shown.
|
||||
*
|
||||
* @return show full usage
|
||||
*/
|
||||
public boolean isFullHelpSuggested() {
|
||||
return fullHelpSuggested;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import com.sk89q.worldedit.util.command.parametric.ParameterException;
|
||||
|
||||
/**
|
||||
* Thrown when there is a missing parameter.
|
||||
*/
|
||||
public class MissingParameterException extends ParameterException {
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Always returns an empty list of suggestions.
|
||||
*/
|
||||
public class NullCompleter implements CommandCompleter {
|
||||
|
||||
@Override
|
||||
public List<String> getSuggestions(String arguments, CommandLocals locals) throws CommandException {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
/**
|
||||
* Describes a parameter.
|
||||
*
|
||||
* @see Description
|
||||
*/
|
||||
public interface Parameter {
|
||||
|
||||
/**
|
||||
* The name of the parameter.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Get the flag associated with this parameter.
|
||||
*
|
||||
* @return the flag, or null if there is no flag associated
|
||||
* @see #isValueFlag()
|
||||
*/
|
||||
Character getFlag();
|
||||
|
||||
/**
|
||||
* Return whether the flag is a value flag.
|
||||
*
|
||||
* @return true if the flag is a value flag
|
||||
* @see #getFlag()
|
||||
*/
|
||||
boolean isValueFlag();
|
||||
|
||||
/**
|
||||
* Get whether this parameter is optional.
|
||||
*
|
||||
* @return true if the parameter does not have to be specified
|
||||
*/
|
||||
boolean isOptional();
|
||||
|
||||
/**
|
||||
* Get the default value as a string to be parsed by the binding.
|
||||
*
|
||||
* @return a default value, or null if none is set
|
||||
*/
|
||||
public String[] getDefaultValue();
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Comparator;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Compares the primary aliases of two {@link CommandMapping} using
|
||||
* {@link String#compareTo(String)}.
|
||||
*/
|
||||
public final class PrimaryAliasComparator implements Comparator<CommandMapping> {
|
||||
|
||||
/**
|
||||
* An instance of this class.
|
||||
*/
|
||||
public static final PrimaryAliasComparator INSTANCE = new PrimaryAliasComparator(null);
|
||||
private final @Nullable Pattern removalPattern;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param removalPattern a regex to remove unwanted characters from the compared aliases
|
||||
*/
|
||||
public PrimaryAliasComparator(@Nullable Pattern removalPattern) {
|
||||
this.removalPattern = removalPattern;
|
||||
}
|
||||
|
||||
private String clean(String alias) {
|
||||
if (removalPattern != null) {
|
||||
return removalPattern.matcher(alias).replaceAll("");
|
||||
}
|
||||
return alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(CommandMapping o1, CommandMapping o2) {
|
||||
return clean(o1.getPrimaryAlias()).compareTo(clean(o2.getPrimaryAlias()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Tracks a command registration.
|
||||
*/
|
||||
public class SimpleCommandMapping implements CommandMapping {
|
||||
|
||||
private final String[] aliases;
|
||||
private final CommandCallable callable;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param callable the command callable
|
||||
* @param alias a list of all aliases, where the first one is the primary one
|
||||
*/
|
||||
public SimpleCommandMapping(CommandCallable callable, String... alias) {
|
||||
super();
|
||||
this.aliases = alias;
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrimaryAlias() {
|
||||
return aliases[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAllAliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandCallable getCallable() {
|
||||
return callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
return getCallable().getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CommandMapping{" +
|
||||
"aliases=" + Arrays.toString(aliases) +
|
||||
", callable=" + callable +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple implementation of {@link Description} which has setters.
|
||||
*/
|
||||
public class SimpleDescription implements Description {
|
||||
|
||||
private List<Parameter> parameters = new ArrayList<Parameter>();
|
||||
private List<String> permissions = new ArrayList<String>();
|
||||
private String description;
|
||||
private String help;
|
||||
private String overrideUsage;
|
||||
|
||||
@Override
|
||||
public List<Parameter> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of parameters.
|
||||
*
|
||||
* @param parameters the list of parameters
|
||||
* @see #getParameters()
|
||||
*/
|
||||
public void setParameters(List<Parameter> parameters) {
|
||||
this.parameters = Collections.unmodifiableList(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the description of the command.
|
||||
*
|
||||
* @param description the description
|
||||
* @see #getShortDescription()
|
||||
*/
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return help;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the help text of the command.
|
||||
*
|
||||
* @param help the help text
|
||||
* @see #getHelp()
|
||||
*/
|
||||
public void setHelp(String help) {
|
||||
this.help = help;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the permissions of this command.
|
||||
*
|
||||
* @param permissions the permissions
|
||||
*/
|
||||
public void setPermissions(List<String> permissions) {
|
||||
this.permissions = Collections.unmodifiableList(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the usage string returned with a given one.
|
||||
*
|
||||
* @param usage usage string, or null
|
||||
*/
|
||||
public void overrideUsage(String usage) {
|
||||
this.overrideUsage = usage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsage() {
|
||||
if (overrideUsage != null) {
|
||||
return overrideUsage;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean first = true;
|
||||
|
||||
for (Parameter parameter : parameters) {
|
||||
if (!first) {
|
||||
builder.append(" ");
|
||||
}
|
||||
builder.append(parameter);
|
||||
first = false;
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getUsage();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
|
||||
import com.sk89q.minecraft.util.commands.WrappedCommandException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A simple implementation of {@link Dispatcher}.
|
||||
*/
|
||||
public class SimpleDispatcher implements Dispatcher {
|
||||
|
||||
private final Map<String, CommandMapping> commands = new HashMap<String, CommandMapping>();
|
||||
private final SimpleDescription description = new SimpleDescription();
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
public SimpleDispatcher() {
|
||||
description.getParameters().add(new SimpleParameter("subcommand"));
|
||||
SimpleParameter extraArgs = new SimpleParameter("...");
|
||||
extraArgs.setOptional(true);
|
||||
description.getParameters().add(extraArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCommand(CommandCallable callable, String... alias) {
|
||||
CommandMapping mapping = new SimpleCommandMapping(callable, alias);
|
||||
|
||||
// Check for replacements
|
||||
for (String a : alias) {
|
||||
String lower = a.toLowerCase();
|
||||
if (commands.containsKey(lower)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Replacing commands is currently undefined behavior");
|
||||
}
|
||||
}
|
||||
|
||||
for (String a : alias) {
|
||||
String lower = a.toLowerCase();
|
||||
commands.put(lower, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CommandMapping> getCommands() {
|
||||
return Collections.unmodifiableSet(new HashSet<CommandMapping>(commands.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAliases() {
|
||||
return Collections.unmodifiableSet(commands.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPrimaryAliases() {
|
||||
Set<String> aliases = new HashSet<String>();
|
||||
for (CommandMapping mapping : getCommands()) {
|
||||
aliases.add(mapping.getPrimaryAlias());
|
||||
}
|
||||
return Collections.unmodifiableSet(aliases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String alias) {
|
||||
return commands.containsKey(alias.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandMapping get(String alias) {
|
||||
return commands.get(alias.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean call(String arguments, CommandLocals locals, String[] parentCommands) throws CommandException {
|
||||
// We have permission for this command if we have permissions for subcommands
|
||||
if (!testPermission(locals)) {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
|
||||
String[] split = CommandContext.split(arguments);
|
||||
Set<String> aliases = getPrimaryAliases();
|
||||
|
||||
if (aliases.isEmpty()) {
|
||||
throw new InvalidUsageException("This command has no sub-commands.", this);
|
||||
} else if (split.length > 0) {
|
||||
String subCommand = split[0];
|
||||
String subArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length));
|
||||
String[] subParents = Arrays.copyOf(parentCommands, parentCommands.length + 1);
|
||||
subParents[parentCommands.length] = subCommand;
|
||||
CommandMapping mapping = get(subCommand);
|
||||
|
||||
if (mapping != null) {
|
||||
try {
|
||||
mapping.getCallable().call(subArguments, locals, subParents);
|
||||
} catch (CommandException e) {
|
||||
e.prependStack(subCommand);
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
throw new WrappedCommandException(t);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
throw new InvalidUsageException("Please choose a sub-command.", this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSuggestions(String arguments, CommandLocals locals) throws CommandException {
|
||||
String[] split = CommandContext.split(arguments);
|
||||
|
||||
if (split.length <= 1) {
|
||||
String prefix = split.length > 0 ? split[0] : "";
|
||||
|
||||
List<String> suggestions = new ArrayList<String>();
|
||||
|
||||
for (CommandMapping mapping : getCommands()) {
|
||||
if (mapping.getCallable().testPermission(locals)) {
|
||||
for (String alias : mapping.getAllAliases()) {
|
||||
if (prefix.isEmpty() || alias.startsWith(arguments)) {
|
||||
suggestions.add(mapping.getPrimaryAlias());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
} else {
|
||||
String subCommand = split[0];
|
||||
CommandMapping mapping = get(subCommand);
|
||||
String passedArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length));
|
||||
|
||||
if (mapping != null) {
|
||||
return mapping.getCallable().getSuggestions(passedArguments, locals);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleDescription getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testPermission(CommandLocals locals) {
|
||||
for (CommandMapping mapping : getCommands()) {
|
||||
if (mapping.getCallable().testPermission(locals)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
/**
|
||||
* A simple implementation of {@link Parameter} that has setters.
|
||||
*/
|
||||
public class SimpleParameter implements Parameter {
|
||||
|
||||
private String name;
|
||||
private Character flag;
|
||||
private boolean isValue;
|
||||
private boolean isOptional;
|
||||
private String[] defaultValue;
|
||||
|
||||
/**
|
||||
* Create a new parameter with no name defined yet.
|
||||
*/
|
||||
public SimpleParameter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new parameter of the given name.
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
public SimpleParameter(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the parameter.
|
||||
*
|
||||
* @param name the parameter name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character getFlag() {
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValueFlag() {
|
||||
return flag != null && isValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flag used by this parameter.
|
||||
*
|
||||
* @param flag the flag, or null if there is no flag
|
||||
* @param isValue true if the flag is a value flag
|
||||
*/
|
||||
public void setFlag(Character flag, boolean isValue) {
|
||||
this.flag = flag;
|
||||
this.isValue = isValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOptional() {
|
||||
return isOptional || getFlag() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this parameter is optional.
|
||||
*
|
||||
* @param isOptional true if this parameter is optional
|
||||
*/
|
||||
public void setOptional(boolean isOptional) {
|
||||
this.isOptional = isOptional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default value.
|
||||
*
|
||||
* @param defaultValue a default value, or null if none
|
||||
*/
|
||||
public void setDefaultValue(String[] defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (getFlag() != null) {
|
||||
if (isValueFlag()) {
|
||||
builder.append("[-")
|
||||
.append(getFlag()).append(" <").append(getName()).append(">]");
|
||||
} else {
|
||||
builder.append("[-").append(getFlag()).append("]");
|
||||
}
|
||||
} else {
|
||||
if (isOptional()) {
|
||||
builder.append("[<").append(getName()).append(">]");
|
||||
} else {
|
||||
builder.append("<").append(getName()).append(">");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command;
|
||||
|
||||
import com.sk89q.worldedit.util.command.parametric.ParameterException;
|
||||
|
||||
/**
|
||||
* Thrown when there are leftover parameters that were not consumed, particular in the
|
||||
* case of the user providing too many parameters.
|
||||
*/
|
||||
public class UnconsumedParameterException extends ParameterException {
|
||||
|
||||
private String unconsumed;
|
||||
|
||||
public UnconsumedParameterException(String unconsumed) {
|
||||
this.unconsumed = unconsumed;
|
||||
}
|
||||
|
||||
public String getUnconsumed() {
|
||||
return unconsumed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.binding;
|
||||
|
||||
import com.sk89q.worldedit.internal.expression.Expression;
|
||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
|
||||
import com.sk89q.worldedit.util.command.parametric.ArgumentStack;
|
||||
import com.sk89q.worldedit.util.command.parametric.BindingBehavior;
|
||||
import com.sk89q.worldedit.util.command.parametric.BindingHelper;
|
||||
import com.sk89q.worldedit.util.command.parametric.BindingMatch;
|
||||
import com.sk89q.worldedit.util.command.parametric.ParameterException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* Handles basic Java types such as {@link String}s, {@link Byte}s, etc.
|
||||
*
|
||||
* <p>Handles both the object and primitive types.</p>
|
||||
*/
|
||||
public final class PrimitiveBindings extends BindingHelper {
|
||||
|
||||
/**
|
||||
* Gets a type from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param context the context
|
||||
* @param text the text annotation
|
||||
* @param modifiers a list of modifiers
|
||||
* @return the requested type
|
||||
* @throws ParameterException on error
|
||||
*/
|
||||
@BindingMatch(classifier = Text.class,
|
||||
type = String.class,
|
||||
behavior = BindingBehavior.CONSUMES,
|
||||
consumedCount = -1,
|
||||
provideModifiers = true)
|
||||
public String getText(ArgumentStack context, Text text, Annotation[] modifiers)
|
||||
throws ParameterException {
|
||||
String v = context.remaining();
|
||||
validate(v, modifiers);
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param context the context
|
||||
* @param modifiers a list of modifiers
|
||||
* @return the requested type
|
||||
* @throws ParameterException on error
|
||||
*/
|
||||
@BindingMatch(type = String.class,
|
||||
behavior = BindingBehavior.CONSUMES,
|
||||
consumedCount = 1,
|
||||
provideModifiers = true)
|
||||
public String getString(ArgumentStack context, Annotation[] modifiers)
|
||||
throws ParameterException {
|
||||
String v = context.next();
|
||||
validate(v, modifiers);
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param context the context
|
||||
* @return the requested type
|
||||
* @throws ParameterException on error
|
||||
*/
|
||||
@BindingMatch(type = { Boolean.class, boolean.class },
|
||||
behavior = BindingBehavior.CONSUMES,
|
||||
consumedCount = 1)
|
||||
public Boolean getBoolean(ArgumentStack context) throws ParameterException {
|
||||
return context.nextBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse numeric input as either a number or a mathematical expression.
|
||||
*
|
||||
* @param input input
|
||||
* @return a number
|
||||
* @throws ParameterException thrown on parse error
|
||||
*/
|
||||
private @Nullable Double parseNumericInput(@Nullable String input) throws ParameterException {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return Double.parseDouble(input);
|
||||
} catch (NumberFormatException e1) {
|
||||
try {
|
||||
Expression expression = Expression.compile(input);
|
||||
return expression.evaluate();
|
||||
} catch (EvaluationException e) {
|
||||
throw new ParameterException(String.format(
|
||||
"Expected '%s' to be a valid number (or a valid mathematical expression)", input));
|
||||
} catch (ExpressionException e) {
|
||||
throw new ParameterException(String.format(
|
||||
"Expected '%s' to be a number or valid math expression (error: %s)", input, e.getMessage()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param context the context
|
||||
* @param modifiers a list of modifiers
|
||||
* @return the requested type
|
||||
* @throws ParameterException on error
|
||||
*/
|
||||
@BindingMatch(type = { Integer.class, int.class },
|
||||
behavior = BindingBehavior.CONSUMES,
|
||||
consumedCount = 1,
|
||||
provideModifiers = true)
|
||||
public Integer getInteger(ArgumentStack context, Annotation[] modifiers) throws ParameterException {
|
||||
Double v = parseNumericInput(context.next());
|
||||
if (v != null) {
|
||||
int intValue = v.intValue();
|
||||
validate(intValue, modifiers);
|
||||
return intValue;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param context the context
|
||||
* @param modifiers a list of modifiers
|
||||
* @return the requested type
|
||||
* @throws ParameterException on error
|
||||
*/
|
||||
@BindingMatch(type = { Short.class, short.class },
|
||||
behavior = BindingBehavior.CONSUMES,
|
||||
consumedCount = 1,
|
||||
provideModifiers = true)
|
||||
public Short getShort(ArgumentStack context, Annotation[] modifiers) throws ParameterException {
|
||||
Integer v = getInteger(context, modifiers);
|
||||
if (v != null) {
|
||||
return v.shortValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param context the context
|
||||
* @param modifiers a list of modifiers
|
||||
* @return the requested type
|
||||
* @throws ParameterException on error
|
||||
*/
|
||||
@BindingMatch(type = { Double.class, double.class },
|
||||
behavior = BindingBehavior.CONSUMES,
|
||||
consumedCount = 1,
|
||||
provideModifiers = true)
|
||||
public Double getDouble(ArgumentStack context, Annotation[] modifiers) throws ParameterException {
|
||||
Double v = parseNumericInput(context.next());
|
||||
if (v != null) {
|
||||
validate(v, modifiers);
|
||||
return v;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a type from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param context the context
|
||||
* @param modifiers a list of modifiers
|
||||
* @return the requested type
|
||||
* @throws ParameterException on error
|
||||
*/
|
||||
@BindingMatch(type = { Float.class, float.class },
|
||||
behavior = BindingBehavior.CONSUMES,
|
||||
consumedCount = 1,
|
||||
provideModifiers = true)
|
||||
public Float getFloat(ArgumentStack context, Annotation[] modifiers) throws ParameterException {
|
||||
Double v = getDouble(context, modifiers);
|
||||
if (v != null) {
|
||||
return v.floatValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a number value using relevant modifiers.
|
||||
*
|
||||
* @param number the number
|
||||
* @param modifiers the list of modifiers to scan
|
||||
* @throws ParameterException on a validation error
|
||||
*/
|
||||
private static void validate(double number, Annotation[] modifiers)
|
||||
throws ParameterException {
|
||||
for (Annotation modifier : modifiers) {
|
||||
if (modifier instanceof Range) {
|
||||
Range range = (Range) modifier;
|
||||
if (number < range.min()) {
|
||||
throw new ParameterException(
|
||||
String.format(
|
||||
"A valid value is greater than or equal to %s " +
|
||||
"(you entered %s)", range.min(), number));
|
||||
} else if (number > range.max()) {
|
||||
throw new ParameterException(
|
||||
String.format(
|
||||
"A valid value is less than or equal to %s " +
|
||||
"(you entered %s)", range.max(), number));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a number value using relevant modifiers.
|
||||
*
|
||||
* @param number the number
|
||||
* @param modifiers the list of modifiers to scan
|
||||
* @throws ParameterException on a validation error
|
||||
*/
|
||||
private static void validate(int number, Annotation[] modifiers)
|
||||
throws ParameterException {
|
||||
for (Annotation modifier : modifiers) {
|
||||
if (modifier instanceof Range) {
|
||||
Range range = (Range) modifier;
|
||||
if (number < range.min()) {
|
||||
throw new ParameterException(
|
||||
String.format(
|
||||
"A valid value is greater than or equal to %s " +
|
||||
"(you entered %s)", range.min(), number));
|
||||
} else if (number > range.max()) {
|
||||
throw new ParameterException(
|
||||
String.format(
|
||||
"A valid value is less than or equal to %s " +
|
||||
"(you entered %s)", range.max(), number));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a string value using relevant modifiers.
|
||||
*
|
||||
* @param string the string
|
||||
* @param modifiers the list of modifiers to scan
|
||||
* @throws ParameterException on a validation error
|
||||
*/
|
||||
private static void validate(String string, Annotation[] modifiers)
|
||||
throws ParameterException {
|
||||
if (string == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Annotation modifier : modifiers) {
|
||||
if (modifier instanceof Validate) {
|
||||
Validate validate = (Validate) modifier;
|
||||
|
||||
if (!validate.regex().isEmpty()) {
|
||||
if (!string.matches(validate.regex())) {
|
||||
throw new ParameterException(
|
||||
String.format(
|
||||
"The given text doesn't match the right " +
|
||||
"format (technically speaking, the 'format' is %s)",
|
||||
validate.regex()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.binding;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Specifies a range of values for numbers.
|
||||
*
|
||||
* @see PrimitiveBindings a user of this annotation as a modifier
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Range {
|
||||
|
||||
/**
|
||||
* The minimum value that the number can be at, inclusive.
|
||||
*
|
||||
* @return the minimum value
|
||||
*/
|
||||
double min() default Double.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* The maximum value that the number can be at, inclusive.
|
||||
*
|
||||
* @return the maximum value
|
||||
*/
|
||||
double max() default Double.MAX_VALUE;
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.binding;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.worldedit.util.command.parametric.BindingBehavior;
|
||||
import com.sk89q.worldedit.util.command.parametric.BindingHelper;
|
||||
import com.sk89q.worldedit.util.command.parametric.BindingMatch;
|
||||
import com.sk89q.worldedit.util.command.parametric.ArgumentStack;
|
||||
|
||||
/**
|
||||
* Standard bindings that should be available to most configurations.
|
||||
*/
|
||||
public final class StandardBindings extends BindingHelper {
|
||||
|
||||
/**
|
||||
* Gets a {@link CommandContext} from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param context the context
|
||||
* @return a selection
|
||||
*/
|
||||
@BindingMatch(type = CommandContext.class,
|
||||
behavior = BindingBehavior.PROVIDES)
|
||||
public CommandContext getCommandContext(ArgumentStack context) {
|
||||
context.markConsumed(); // Consume entire stack
|
||||
return context.getContext();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.binding;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates a command flag, such as {@code /command -f}.
|
||||
*
|
||||
* <p>If used on a boolean type, then the flag will be a non-value flag. If
|
||||
* used on any other type, then the flag will be a value flag.</p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Switch {
|
||||
|
||||
/**
|
||||
* The flag character.
|
||||
*
|
||||
* @return the flag character (A-Z a-z 0-9 is acceptable)
|
||||
*/
|
||||
char value();
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.binding;
|
||||
|
||||
import com.sk89q.worldedit.util.command.parametric.ArgumentStack;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates a {@link String} parameter will call {@link ArgumentStack#remaining()} and
|
||||
* therefore consume all remaining arguments.
|
||||
*
|
||||
* <p>This should only be used at the end of a list of parameters (of parameters that
|
||||
* need to consume from the stack of arguments), otherwise following parameters will
|
||||
* have no values left to consume.</p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Text {
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.binding;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Used to validate a string.
|
||||
*
|
||||
* @see PrimitiveBindings where this validation is used
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Validate {
|
||||
|
||||
/**
|
||||
* An optional regular expression that must match the string.
|
||||
*
|
||||
* @see Pattern regular expression class
|
||||
* @return the pattern
|
||||
*/
|
||||
String regex() default "";
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.fluent;
|
||||
|
||||
import com.sk89q.worldedit.util.command.Dispatcher;
|
||||
import com.sk89q.worldedit.util.command.SimpleDispatcher;
|
||||
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
|
||||
|
||||
/**
|
||||
* A fluent interface to creating a command graph.
|
||||
*
|
||||
* <p>A command graph may have multiple commands, and multiple sub-commands below that,
|
||||
* and possibly below that.</p>
|
||||
*/
|
||||
public class CommandGraph {
|
||||
|
||||
private final DispatcherNode rootDispatcher;
|
||||
private ParametricBuilder builder;
|
||||
|
||||
/**
|
||||
* Create a new command graph.
|
||||
*/
|
||||
public CommandGraph() {
|
||||
SimpleDispatcher dispatcher = new SimpleDispatcher();
|
||||
rootDispatcher = new DispatcherNode(this, null, dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root dispatcher node.
|
||||
*
|
||||
* @return the root dispatcher node
|
||||
*/
|
||||
public DispatcherNode commands() {
|
||||
return rootDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ParametricBuilder}.
|
||||
*
|
||||
* @return the builder, or null.
|
||||
*/
|
||||
public ParametricBuilder getBuilder() {
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ParametricBuilder} used for calls to
|
||||
* {@link DispatcherNode#registerMethods(Object)}.
|
||||
*
|
||||
* @param builder the builder, or null
|
||||
* @return this object
|
||||
*/
|
||||
public CommandGraph builder(ParametricBuilder builder) {
|
||||
this.builder = builder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root dispatcher.
|
||||
*
|
||||
* @return the root dispatcher
|
||||
*/
|
||||
public Dispatcher getDispatcher() {
|
||||
return rootDispatcher.getDispatcher();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.fluent;
|
||||
|
||||
import com.sk89q.worldedit.util.command.CommandCallable;
|
||||
import com.sk89q.worldedit.util.command.Dispatcher;
|
||||
import com.sk89q.worldedit.util.command.SimpleDispatcher;
|
||||
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
|
||||
|
||||
/**
|
||||
* A collection of commands.
|
||||
*/
|
||||
public class DispatcherNode {
|
||||
|
||||
private final CommandGraph graph;
|
||||
private final DispatcherNode parent;
|
||||
private final SimpleDispatcher dispatcher;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param graph the root fluent graph object
|
||||
* @param parent the parent node, or null
|
||||
* @param dispatcher the dispatcher for this node
|
||||
*/
|
||||
DispatcherNode(CommandGraph graph, DispatcherNode parent,
|
||||
SimpleDispatcher dispatcher) {
|
||||
this.graph = graph;
|
||||
this.parent = parent;
|
||||
this.dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the description.
|
||||
*
|
||||
* <p>This can only be used on {@link DispatcherNode}s returned by
|
||||
* {@link #group(String...)}.</p>
|
||||
*
|
||||
* @param description the description
|
||||
* @return this object
|
||||
*/
|
||||
public DispatcherNode describeAs(String description) {
|
||||
dispatcher.getDescription().setDescription(description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a command with this dispatcher.
|
||||
*
|
||||
* @param callable the executor
|
||||
* @param alias the list of aliases, where the first alias is the primary one
|
||||
*/
|
||||
public void register(CommandCallable callable, String... alias) {
|
||||
dispatcher.registerCommand(callable, alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and register a command with this dispatcher using the
|
||||
* {@link ParametricBuilder} assigned on the root {@link CommandGraph}.
|
||||
*
|
||||
* @param object the object provided to the {@link ParametricBuilder}
|
||||
* @return this object
|
||||
* @see ParametricBuilder#registerMethodsAsCommands(com.sk89q.worldedit.util.command.Dispatcher, Object)
|
||||
*/
|
||||
public DispatcherNode registerMethods(Object object) {
|
||||
ParametricBuilder builder = graph.getBuilder();
|
||||
if (builder == null) {
|
||||
throw new RuntimeException("No ParametricBuilder set");
|
||||
}
|
||||
builder.registerMethodsAsCommands(getDispatcher(), object);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new command that will contain sub-commands.
|
||||
*
|
||||
* <p>The object returned by this method can be used to add sub-commands. To
|
||||
* return to this "parent" context, use {@link DispatcherNode#graph()}.</p>
|
||||
*
|
||||
* @param alias the list of aliases, where the first alias is the primary one
|
||||
* @return an object to place sub-commands
|
||||
*/
|
||||
public DispatcherNode group(String... alias) {
|
||||
SimpleDispatcher command = new SimpleDispatcher();
|
||||
getDispatcher().registerCommand(command, alias);
|
||||
return new DispatcherNode(graph, this, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent node.
|
||||
*
|
||||
* @return the parent node
|
||||
* @throws RuntimeException if there is no parent node.
|
||||
*/
|
||||
public DispatcherNode parent() {
|
||||
if (parent != null) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
throw new RuntimeException("This node does not have a parent");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root command graph.
|
||||
*
|
||||
* @return the root command graph
|
||||
*/
|
||||
public CommandGraph graph() {
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying dispatcher of this object.
|
||||
*
|
||||
* @return the dispatcher
|
||||
*/
|
||||
public Dispatcher getDispatcher() {
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.worldedit.util.command.SimpleDescription;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* An abstract listener.
|
||||
*/
|
||||
public abstract class AbstractInvokeListener implements InvokeListener {
|
||||
|
||||
@Override
|
||||
public void updateDescription(Object object, Method method,
|
||||
ParameterData[] parameters, SimpleDescription description) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
|
||||
public interface ArgumentStack {
|
||||
|
||||
/**
|
||||
* Get the next string, which may come from the stack or a value flag.
|
||||
*
|
||||
* @return the value
|
||||
* @throws ParameterException on a parameter error
|
||||
*/
|
||||
String next() throws ParameterException;
|
||||
|
||||
/**
|
||||
* Get the next integer, which may come from the stack or a value flag.
|
||||
*
|
||||
* @return the value
|
||||
* @throws ParameterException on a parameter error
|
||||
*/
|
||||
Integer nextInt() throws ParameterException;
|
||||
|
||||
/**
|
||||
* Get the next double, which may come from the stack or a value flag.
|
||||
*
|
||||
* @return the value
|
||||
* @throws ParameterException on a parameter error
|
||||
*/
|
||||
Double nextDouble() throws ParameterException;
|
||||
|
||||
/**
|
||||
* Get the next boolean, which may come from the stack or a value flag.
|
||||
*
|
||||
* @return the value
|
||||
* @throws ParameterException on a parameter error
|
||||
*/
|
||||
Boolean nextBoolean() throws ParameterException;
|
||||
|
||||
/**
|
||||
* Get all remaining string values, which will consume the rest of the stack.
|
||||
*
|
||||
* @return the value
|
||||
* @throws ParameterException on a parameter error
|
||||
*/
|
||||
String remaining() throws ParameterException;
|
||||
|
||||
/**
|
||||
* Set as completely consumed.
|
||||
*/
|
||||
void markConsumed();
|
||||
|
||||
/**
|
||||
* Get the underlying context.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
CommandContext getContext();
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.worldedit.util.command.binding.PrimitiveBindings;
|
||||
import com.sk89q.worldedit.util.command.binding.StandardBindings;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Used to parse user input for a command, based on available method types
|
||||
* and annotations.
|
||||
*
|
||||
* <p>A binding can be used to handle several types at once. For a binding to be
|
||||
* called, it must be registered with a {@link ParametricBuilder} with
|
||||
* {@link ParametricBuilder#addBinding(Binding, java.lang.reflect.Type...)}.</p>
|
||||
*
|
||||
* @see PrimitiveBindings an example of primitive bindings
|
||||
* @see StandardBindings standard bindings
|
||||
*/
|
||||
public interface Binding {
|
||||
|
||||
/**
|
||||
* Get the types that this binding handles.
|
||||
*
|
||||
* @return the types
|
||||
*/
|
||||
Type[] getTypes();
|
||||
|
||||
/**
|
||||
* Get how this binding consumes from a {@link ArgumentStack}.
|
||||
*
|
||||
* @param parameter information about the parameter
|
||||
* @return the behavior
|
||||
*/
|
||||
BindingBehavior getBehavior(ParameterData parameter);
|
||||
|
||||
/**
|
||||
* Get the number of arguments that this binding will consume, if this
|
||||
* information is available.
|
||||
*
|
||||
* <p>This method must return -1 for binding behavior types that are not
|
||||
* {@link BindingBehavior#CONSUMES}.</p>
|
||||
*
|
||||
* @param parameter information about the parameter
|
||||
* @return the number of consumed arguments, or -1 if unknown or irrelevant
|
||||
*/
|
||||
int getConsumedCount(ParameterData parameter);
|
||||
|
||||
/**
|
||||
* Attempt to consume values (if required) from the given {@link ArgumentStack}
|
||||
* in order to instantiate an object for the given parameter.
|
||||
*
|
||||
* @param parameter information about the parameter
|
||||
* @param scoped the arguments the user has input
|
||||
* @param onlyConsume true to only consume arguments
|
||||
* @return an object parsed for the given parameter
|
||||
* @throws ParameterException thrown if the parameter could not be formulated
|
||||
* @throws CommandException on a command exception
|
||||
*/
|
||||
Object bind(ParameterData parameter, ArgumentStack scoped, boolean onlyConsume)
|
||||
throws ParameterException, CommandException, InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Get a list of suggestions for the given parameter and user arguments.
|
||||
*
|
||||
* @param parameter information about the parameter
|
||||
* @param prefix what the user has typed so far (may be an empty string)
|
||||
* @return a list of suggestions
|
||||
*/
|
||||
List<String> getSuggestions(ParameterData parameter, String prefix);
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
import com.sk89q.worldedit.util.command.binding.Switch;
|
||||
|
||||
/**
|
||||
* Determines the type of binding.
|
||||
*/
|
||||
public enum BindingBehavior {
|
||||
|
||||
/**
|
||||
* Always consumes from a {@link ArgumentStack}.
|
||||
*/
|
||||
CONSUMES,
|
||||
|
||||
/**
|
||||
* Sometimes consumes from a {@link ArgumentStack}.
|
||||
*
|
||||
* <p>Bindings that exhibit this behavior must be defined as a {@link Switch}
|
||||
* by commands utilizing the given binding.</p>
|
||||
*/
|
||||
INDETERMINATE,
|
||||
|
||||
/**
|
||||
* Never consumes from a {@link ArgumentStack}.
|
||||
*
|
||||
* <p>Bindings that exhibit this behavior generate objects from other sources,
|
||||
* such as from a {@link CommandLocals}. These are "magic" bindings that inject
|
||||
* variables.</p>
|
||||
*/
|
||||
PROVIDES
|
||||
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A binding helper that uses the {@link BindingMatch} annotation to make
|
||||
* writing bindings extremely easy.
|
||||
*
|
||||
* <p>Methods must have the following and only the following parameters:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>A {@link ArgumentStack}</li>
|
||||
* <li>A {@link Annotation} <strong>if there is a classifier set</strong></li>
|
||||
* <li>A {@link Annotation}[]
|
||||
* <strong>if there {@link BindingMatch#provideModifiers()} is true</strong></li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Methods may throw any exception. Exceptions may be converted using a
|
||||
* {@link ExceptionConverter} registered with the {@link ParametricBuilder}.</p>
|
||||
*/
|
||||
public class BindingHelper implements Binding {
|
||||
|
||||
private final List<BoundMethod> bindings;
|
||||
private final Type[] types;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
public BindingHelper() {
|
||||
List<BoundMethod> bindings = new ArrayList<BoundMethod>();
|
||||
List<Type> types = new ArrayList<Type>();
|
||||
|
||||
for (Method method : this.getClass().getMethods()) {
|
||||
BindingMatch info = method.getAnnotation(BindingMatch.class);
|
||||
if (info != null) {
|
||||
Class<? extends Annotation> classifier = null;
|
||||
|
||||
// Set classifier
|
||||
if (!info.classifier().equals(Annotation.class)) {
|
||||
classifier = info.classifier();
|
||||
types.add(classifier);
|
||||
}
|
||||
|
||||
for (Type t : info.type()) {
|
||||
Type type = null;
|
||||
|
||||
// Set type
|
||||
if (!t.equals(Class.class)) {
|
||||
type = t;
|
||||
if (classifier == null) {
|
||||
types.add(type); // Only if there is no classifier set!
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if at least one is set
|
||||
if (type == null && classifier == null) {
|
||||
throw new RuntimeException(
|
||||
"A @BindingMatch needs either a type or classifier set");
|
||||
}
|
||||
|
||||
BoundMethod handler = new BoundMethod(info, type, classifier, method);
|
||||
bindings.add(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(bindings);
|
||||
|
||||
this.bindings = bindings;
|
||||
|
||||
Type[] typesArray = new Type[types.size()];
|
||||
types.toArray(typesArray);
|
||||
this.types = typesArray;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a {@link BindingMatch} according to the given parameter.
|
||||
*
|
||||
* @param parameter the parameter
|
||||
* @return a binding
|
||||
*/
|
||||
private BoundMethod match(ParameterData parameter) {
|
||||
for (BoundMethod binding : bindings) {
|
||||
Annotation classifer = parameter.getClassifier();
|
||||
Type type = parameter.getType();
|
||||
|
||||
if (binding.classifier != null) {
|
||||
if (classifer != null && classifer.annotationType().equals(binding.classifier)) {
|
||||
if (binding.type == null || binding.type.equals(type)) {
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
} else if (binding.type.equals(type)) {
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unknown type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type[] getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConsumedCount(ParameterData parameter) {
|
||||
return match(parameter).annotation.consumedCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingBehavior getBehavior(ParameterData parameter) {
|
||||
return match(parameter).annotation.behavior();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object bind(ParameterData parameter, ArgumentStack scoped,
|
||||
boolean onlyConsume) throws ParameterException, CommandException, InvocationTargetException {
|
||||
BoundMethod binding = match(parameter);
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add(scoped);
|
||||
|
||||
if (binding.classifier != null) {
|
||||
args.add(parameter.getClassifier());
|
||||
}
|
||||
|
||||
if (binding.annotation.provideModifiers()) {
|
||||
args.add(parameter.getModifiers());
|
||||
}
|
||||
|
||||
if (onlyConsume && binding.annotation.behavior() == BindingBehavior.PROVIDES) {
|
||||
return null; // Nothing to consume, nothing to do
|
||||
}
|
||||
|
||||
Object[] argsArray = new Object[args.size()];
|
||||
args.toArray(argsArray);
|
||||
|
||||
try {
|
||||
return binding.method.invoke(this, argsArray);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException(
|
||||
"Processing of classifier " + parameter.getClassifier() +
|
||||
" and type " + parameter.getType() + " failed for method\n" +
|
||||
binding.method + "\nbecause the parameters for that method are wrong", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof ParameterException) {
|
||||
throw (ParameterException) e.getCause();
|
||||
} else if (e.getCause() instanceof CommandException) {
|
||||
throw (CommandException) e.getCause();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSuggestions(ParameterData parameter, String prefix) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
private static class BoundMethod implements Comparable<BoundMethod> {
|
||||
private final BindingMatch annotation;
|
||||
private final Type type;
|
||||
private final Class<? extends Annotation> classifier;
|
||||
private final Method method;
|
||||
|
||||
BoundMethod(BindingMatch annotation, Type type,
|
||||
Class<? extends Annotation> classifier, Method method) {
|
||||
this.annotation = annotation;
|
||||
this.type = type;
|
||||
this.classifier = classifier;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(BoundMethod o) {
|
||||
if (classifier != null && o.classifier == null) {
|
||||
return -1;
|
||||
} else if (classifier == null && o.classifier != null) {
|
||||
return 1;
|
||||
} else if (classifier != null && o.classifier != null) {
|
||||
if (type != null && o.type == null) {
|
||||
return -1;
|
||||
} else if (type == null && o.type != null) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Denotes a match of a binding.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface BindingMatch {
|
||||
|
||||
/**
|
||||
* The classifier.
|
||||
*
|
||||
* @return the classifier, or {@link Annotation} if not set
|
||||
*/
|
||||
Class<? extends Annotation> classifier() default Annotation.class;
|
||||
|
||||
/**
|
||||
* The type.
|
||||
*
|
||||
* @return the type, or {@link Class} if not set
|
||||
*/
|
||||
Class<?>[] type() default Class.class;
|
||||
|
||||
/**
|
||||
* The binding behavior.
|
||||
*
|
||||
* @return the behavior
|
||||
*/
|
||||
BindingBehavior behavior();
|
||||
|
||||
/**
|
||||
* Get the number of arguments that this binding consumes.
|
||||
*
|
||||
* @return -1 if unknown or irrelevant
|
||||
*/
|
||||
int consumedCount() default -1;
|
||||
|
||||
/**
|
||||
* Set whether an array of modifier annotations is provided in the list of
|
||||
* arguments.
|
||||
*
|
||||
* @return true to provide modifiers
|
||||
*/
|
||||
boolean provideModifiers() default false;
|
||||
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.worldedit.util.command.MissingParameterException;
|
||||
|
||||
/**
|
||||
* Makes an instance of a {@link CommandContext} into a stack of arguments
|
||||
* that can be consumed.
|
||||
*
|
||||
* @see ParametricBuilder a user of this class
|
||||
*/
|
||||
public class ContextArgumentStack implements ArgumentStack {
|
||||
|
||||
private final CommandContext context;
|
||||
private int index = 0;
|
||||
private int markedIndex = 0;
|
||||
|
||||
/**
|
||||
* Create a new instance using the given context.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public ContextArgumentStack(CommandContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() throws ParameterException {
|
||||
try {
|
||||
return context.getString(index++);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new MissingParameterException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer nextInt() throws ParameterException {
|
||||
try {
|
||||
return Integer.parseInt(next());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParameterException(
|
||||
"Expected a number, got '" + context.getString(index - 1) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double nextDouble() throws ParameterException {
|
||||
try {
|
||||
return Double.parseDouble(next());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParameterException(
|
||||
"Expected a number, got '" + context.getString(index - 1) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean nextBoolean() throws ParameterException {
|
||||
try {
|
||||
return next().equalsIgnoreCase("true");
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new MissingParameterException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remaining() throws ParameterException {
|
||||
try {
|
||||
String value = context.getJoinedStrings(index);
|
||||
index = context.argsLength();
|
||||
return value;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new MissingParameterException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unconsumed arguments left over, without touching the stack.
|
||||
*
|
||||
* @return the unconsumed arguments
|
||||
*/
|
||||
public String getUnconsumed() {
|
||||
if (index >= context.argsLength()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return context.getJoinedStrings(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markConsumed() {
|
||||
index = context.argsLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current position.
|
||||
*
|
||||
* @return the position
|
||||
*/
|
||||
public int position() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the current position of the stack.
|
||||
*
|
||||
* <p>The marked position initially starts at 0.</p>
|
||||
*/
|
||||
public void mark() {
|
||||
markedIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to the previously {@link #mark()}ed position of the stack, and return
|
||||
* the arguments that were consumed between this point and that previous point.
|
||||
*
|
||||
* <p>The marked position initially starts at 0.</p>
|
||||
*
|
||||
* @return the consumed arguments
|
||||
*/
|
||||
public String reset() {
|
||||
String value = context.getString(markedIndex, index);
|
||||
index = markedIndex;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether any arguments were consumed between the marked position
|
||||
* and the current position.
|
||||
*
|
||||
* <p>The marked position initially starts at 0.</p>
|
||||
*
|
||||
* @return true if values were consumed.
|
||||
*/
|
||||
public boolean wasConsumed() {
|
||||
return markedIndex != index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the arguments that were consumed between this point and that marked point.
|
||||
*
|
||||
* <p>The marked position initially starts at 0.</p>
|
||||
*
|
||||
* @return the consumed arguments
|
||||
*/
|
||||
public String getConsumed() {
|
||||
return context.getString(markedIndex, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying context.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
@Override
|
||||
public CommandContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.WrappedCommandException;
|
||||
|
||||
/**
|
||||
* Used to convert a recognized {@link Throwable} into an appropriate
|
||||
* {@link CommandException}.
|
||||
*
|
||||
* <p>Methods (when invoked by a {@link ParametricBuilder}-created command) may throw
|
||||
* relevant exceptions that are not caught by the command manager, but translate
|
||||
* into reasonable exceptions for an application. However, unknown exceptions are
|
||||
* normally simply wrapped in a {@link WrappedCommandException} and bubbled up. Only
|
||||
* normal {@link CommandException}s will be printed correctly, so a converter translates
|
||||
* one of these unknown exceptions into an appropriate {@link CommandException}.</p>
|
||||
*
|
||||
* <p>This also allows the code calling the command to not need be aware of these
|
||||
* application-specific exceptions, as they will all be converted to
|
||||
* {@link CommandException}s that are handled normally.</p>
|
||||
*/
|
||||
public interface ExceptionConverter {
|
||||
|
||||
/**
|
||||
* Attempt to convert the given throwable into a {@link CommandException}.
|
||||
*
|
||||
* <p>If the exception is not recognized, then nothing should be thrown.</p>
|
||||
*
|
||||
* @param t the throwable
|
||||
* @throws CommandException a command exception
|
||||
*/
|
||||
void convert(Throwable t) throws CommandException;
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.WrappedCommandException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An implementation of an {@link ExceptionConverter} that automatically calls
|
||||
* the correct method defined on this object.
|
||||
*
|
||||
* <p>Only public methods will be used. Methods will be called in order of decreasing
|
||||
* levels of inheritance (between classes where one inherits the other). For two
|
||||
* different inheritance branches, the order between them is undefined.</p>
|
||||
*/
|
||||
public abstract class ExceptionConverterHelper implements ExceptionConverter {
|
||||
|
||||
private final List<ExceptionHandler> handlers;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ExceptionConverterHelper() {
|
||||
List<ExceptionHandler> handlers = new ArrayList<ExceptionHandler>();
|
||||
|
||||
for (Method method : this.getClass().getMethods()) {
|
||||
if (method.getAnnotation(ExceptionMatch.class) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<?>[] parameters = method.getParameterTypes();
|
||||
if (parameters.length == 1) {
|
||||
Class<?> cls = parameters[0];
|
||||
if (Throwable.class.isAssignableFrom(cls)) {
|
||||
handlers.add(new ExceptionHandler(
|
||||
(Class<? extends Throwable>) cls, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(handlers);
|
||||
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convert(Throwable t) throws CommandException {
|
||||
Class<?> throwableClass = t.getClass();
|
||||
for (ExceptionHandler handler : handlers) {
|
||||
if (handler.cls.isAssignableFrom(throwableClass)) {
|
||||
try {
|
||||
handler.method.invoke(this, t);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof CommandException) {
|
||||
throw (CommandException) e.getCause();
|
||||
}
|
||||
throw new WrappedCommandException(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WrappedCommandException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new WrappedCommandException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExceptionHandler implements Comparable<ExceptionHandler> {
|
||||
final Class<? extends Throwable> cls;
|
||||
final Method method;
|
||||
|
||||
private ExceptionHandler(Class<? extends Throwable> cls, Method method) {
|
||||
this.cls = cls;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ExceptionHandler o) {
|
||||
if (cls.equals(o.cls)) {
|
||||
return 0;
|
||||
} else if (cls.isAssignableFrom(o.cls)) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Denotes a match of an exception.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ExceptionMatch {
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Called before and after a command is invoked for commands executed by a command
|
||||
* created using {@link ParametricBuilder}.
|
||||
*
|
||||
* <p>Invocation handlers are created by {@link InvokeListener}s. Multiple
|
||||
* listeners and handlers can be registered, and all be run. However, if one handler
|
||||
* throws an exception, future handlers will not execute and the command will
|
||||
* not execute (if thrown in
|
||||
* {@link #preInvoke(Object, Method, ParameterData[], Object[], CommandContext)}).</p>
|
||||
*
|
||||
* @see InvokeListener the factory
|
||||
*/
|
||||
public interface InvokeHandler {
|
||||
|
||||
/**
|
||||
* Called before parameters are processed.
|
||||
*
|
||||
* @param object the object
|
||||
* @param method the method
|
||||
* @param parameters the list of parameters
|
||||
* @param context the context
|
||||
* @throws CommandException can be thrown for an error, which will stop invocation
|
||||
* @throws ParameterException on parameter error
|
||||
*/
|
||||
void preProcess(Object object, Method method, ParameterData[] parameters,
|
||||
CommandContext context) throws CommandException, ParameterException;
|
||||
|
||||
/**
|
||||
* Called before the parameter is invoked.
|
||||
*
|
||||
* @param object the object
|
||||
* @param method the method
|
||||
* @param parameters the list of parameters
|
||||
* @param args the arguments to be given to the method
|
||||
* @param context the context
|
||||
* @throws CommandException can be thrown for an error, which will stop invocation
|
||||
* @throws ParameterException on parameter error
|
||||
*/
|
||||
void preInvoke(Object object, Method method, ParameterData[] parameters,
|
||||
Object[] args, CommandContext context) throws CommandException, ParameterException;
|
||||
|
||||
/**
|
||||
* Called after the parameter is invoked.
|
||||
*
|
||||
* @param object the object
|
||||
* @param method the method
|
||||
* @param parameters the list of parameters
|
||||
* @param args the arguments to be given to the method
|
||||
* @param context the context
|
||||
* @throws CommandException can be thrown for an error
|
||||
* @throws ParameterException on parameter error
|
||||
*/
|
||||
void postInvoke(Object object, Method method, ParameterData[] parameters,
|
||||
Object[] args, CommandContext context) throws CommandException, ParameterException;
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
||||
import com.sk89q.worldedit.util.command.CommandCallable;
|
||||
import com.sk89q.worldedit.util.command.SimpleDescription;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Listens to events related to {@link ParametricBuilder}.
|
||||
*/
|
||||
public interface InvokeListener {
|
||||
|
||||
/**
|
||||
* Create a new invocation handler.
|
||||
*
|
||||
* <p>An example use of an {@link InvokeHandler} would be to verify permissions
|
||||
* added by the {@link CommandPermissions} annotation.</p>
|
||||
*
|
||||
* <p>For simple {@link InvokeHandler}, an object can implement both this
|
||||
* interface and {@link InvokeHandler}.</p>
|
||||
*
|
||||
* @return a new invocation handler
|
||||
*/
|
||||
InvokeHandler createInvokeHandler();
|
||||
|
||||
/**
|
||||
* During creation of a {@link CommandCallable} by a {@link ParametricBuilder},
|
||||
* this will be called in case the description needs to be updated.
|
||||
*
|
||||
* @param object the object
|
||||
* @param method the method
|
||||
* @param parameters a list of parameters
|
||||
* @param description the description to be updated
|
||||
*/
|
||||
void updateDescription(Object object, Method method, ParameterData[] parameters,
|
||||
SimpleDescription description);
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.Command;
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.worldedit.util.command.MissingParameterException;
|
||||
import com.sk89q.worldedit.util.command.SimpleDescription;
|
||||
import com.sk89q.worldedit.util.command.UnconsumedParameterException;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Handles legacy properties on {@link Command} such as {@link Command#min()} and
|
||||
* {@link Command#max()}.
|
||||
*/
|
||||
public class LegacyCommandsHandler extends AbstractInvokeListener implements InvokeHandler {
|
||||
|
||||
@Override
|
||||
public InvokeHandler createInvokeHandler() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preProcess(Object object, Method method,
|
||||
ParameterData[] parameters, CommandContext context)
|
||||
throws CommandException, ParameterException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preInvoke(Object object, Method method,
|
||||
ParameterData[] parameters, Object[] args, CommandContext context)
|
||||
throws ParameterException {
|
||||
Command annotation = method.getAnnotation(Command.class);
|
||||
|
||||
if (annotation != null) {
|
||||
if (context.argsLength() < annotation.min()) {
|
||||
throw new MissingParameterException();
|
||||
}
|
||||
|
||||
if (annotation.max() != -1 && context.argsLength() > annotation.max()) {
|
||||
throw new UnconsumedParameterException(
|
||||
context.getRemainingString(annotation.max()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInvoke(Object object, Method method,
|
||||
ParameterData[] parameters, Object[] args, CommandContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDescription(Object object, Method method,
|
||||
ParameterData[] parameters, SimpleDescription description) {
|
||||
Command annotation = method.getAnnotation(Command.class);
|
||||
|
||||
// Handle the case for old commands where no usage is set and all of its
|
||||
// parameters are provider bindings, so its usage information would
|
||||
// be blank and would imply that there were no accepted parameters
|
||||
if (annotation != null && annotation.usage().isEmpty()
|
||||
&& (annotation.min() > 0 || annotation.max() > 0)) {
|
||||
boolean hasUserParameters = false;
|
||||
|
||||
for (ParameterData parameter : parameters) {
|
||||
if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) {
|
||||
hasUserParameters = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasUserParameters) {
|
||||
description.overrideUsage("(unknown usage information)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates an optional parameter.
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Optional {
|
||||
|
||||
/**
|
||||
* The default value to use if no value is set.
|
||||
*
|
||||
* @return a string value, or an empty list
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.worldedit.util.command.SimpleParameter;
|
||||
import com.sk89q.worldedit.util.command.binding.PrimitiveBindings;
|
||||
import com.sk89q.worldedit.util.command.binding.Range;
|
||||
import com.sk89q.worldedit.util.command.binding.Text;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Describes a parameter in detail.
|
||||
*/
|
||||
public class ParameterData extends SimpleParameter {
|
||||
|
||||
private Binding binding;
|
||||
private Annotation classifier;
|
||||
private Annotation[] modifiers;
|
||||
private Type type;
|
||||
|
||||
/**
|
||||
* Get the binding associated with this parameter.
|
||||
*
|
||||
* @return the binding
|
||||
*/
|
||||
public Binding getBinding() {
|
||||
return binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the binding associated with this parameter.
|
||||
*
|
||||
* @param binding the binding
|
||||
*/
|
||||
void setBinding(Binding binding) {
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the main type of this parameter.
|
||||
*
|
||||
* <p>The type is normally that is used to determine which binding is used
|
||||
* for a particular method's parameter.</p>
|
||||
*
|
||||
* @return the main type
|
||||
* @see #getClassifier() which can override the type
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the main type of this parameter.
|
||||
*
|
||||
* @param type the main type
|
||||
*/
|
||||
void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the classifier annotation.
|
||||
*
|
||||
* <p>Normally, the type determines what binding is called, but classifiers
|
||||
* take precedence if one is found (and registered with
|
||||
* {@link ParametricBuilder#addBinding(Binding, Type...)}).
|
||||
* An example of a classifier annotation is {@link Text}.</p>
|
||||
*
|
||||
* @return the classifier annotation, null is possible
|
||||
*/
|
||||
public Annotation getClassifier() {
|
||||
return classifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the classifier annotation.
|
||||
*
|
||||
* @param classifier the classifier annotation, null is possible
|
||||
*/
|
||||
void setClassifier(Annotation classifier) {
|
||||
this.classifier = classifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of modifier annotations.
|
||||
*
|
||||
* <p>Modifier annotations are not considered in the process of choosing a binding
|
||||
* for a method parameter, but they can be used to modify the behavior of a binding.
|
||||
* An example of a modifier annotation is {@link Range}, which can restrict
|
||||
* numeric values handled by {@link PrimitiveBindings} to be within a range. The list
|
||||
* of annotations may contain a classifier and other unrelated annotations.</p>
|
||||
*
|
||||
* @return a list of annotations
|
||||
*/
|
||||
public Annotation[] getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of modifiers.
|
||||
*
|
||||
* @param modifiers a list of annotations
|
||||
*/
|
||||
void setModifiers(Annotation[] modifiers) {
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of arguments this binding consumes.
|
||||
*
|
||||
* @return -1 if unknown or unavailable
|
||||
*/
|
||||
int getConsumedCount() {
|
||||
return getBinding().getConsumedCount(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether this parameter is entered by the user.
|
||||
*
|
||||
* @return true if this parameter is entered by the user.
|
||||
*/
|
||||
boolean isUserInput() {
|
||||
return getBinding().getBehavior(this) != BindingBehavior.PROVIDES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether this parameter consumes non-flag arguments.
|
||||
*
|
||||
* @return true if this parameter consumes non-flag arguments
|
||||
*/
|
||||
boolean isNonFlagConsumer() {
|
||||
return getBinding().getBehavior(this) != BindingBehavior.PROVIDES && !isValueFlag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate this parameter and its binding.
|
||||
*/
|
||||
void validate(Method method, int parameterIndex) throws ParametricException {
|
||||
// We can't have indeterminate consumers without @Switches otherwise
|
||||
// it may screw up parameter processing for later bindings
|
||||
BindingBehavior behavior = getBinding().getBehavior(this);
|
||||
boolean indeterminate = (behavior == BindingBehavior.INDETERMINATE);
|
||||
if (!isValueFlag() && indeterminate) {
|
||||
throw new ParametricException(
|
||||
"@Switch missing for indeterminate consumer\n\n" +
|
||||
"Notably:\nFor the type " + type + ", the binding " +
|
||||
getBinding().getClass().getCanonicalName() +
|
||||
"\nmay or may not consume parameters (isIndeterminateConsumer(" + type + ") = true)" +
|
||||
"\nand therefore @Switch(flag) is required for parameter #" + parameterIndex + " of \n" +
|
||||
method.toGenericString());
|
||||
}
|
||||
|
||||
// getConsumedCount() better return -1 if the BindingBehavior is not CONSUMES
|
||||
if (behavior != BindingBehavior.CONSUMES && binding.getConsumedCount(this) != -1) {
|
||||
throw new ParametricException(
|
||||
"getConsumedCount() does not return -1 for binding " +
|
||||
getBinding().getClass().getCanonicalName() +
|
||||
"\neven though its behavior type is " + behavior.name() +
|
||||
"\nfor parameter #" + parameterIndex + " of \n" +
|
||||
method.toGenericString());
|
||||
}
|
||||
|
||||
// getConsumedCount() should not return 0 if the BindingBehavior is not PROVIDES
|
||||
if (behavior != BindingBehavior.PROVIDES && binding.getConsumedCount(this) == 0) {
|
||||
throw new ParametricException(
|
||||
"getConsumedCount() must not return 0 for binding " +
|
||||
getBinding().getClass().getCanonicalName() +
|
||||
"\nwhen its behavior type is " + behavior.name() + " and not PROVIDES " +
|
||||
"\nfor parameter #" + parameterIndex + " of \n" +
|
||||
method.toGenericString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
/**
|
||||
* Thrown if there is an error with a parameter.
|
||||
*/
|
||||
public class ParameterException extends Exception {
|
||||
|
||||
public ParameterException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ParameterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ParameterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ParameterException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.google.common.collect.ImmutableBiMap.Builder;
|
||||
import com.sk89q.minecraft.util.commands.Command;
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
||||
import com.sk89q.worldedit.util.auth.Authorizer;
|
||||
import com.sk89q.worldedit.util.auth.NullAuthorizer;
|
||||
import com.sk89q.worldedit.util.command.CommandCallable;
|
||||
import com.sk89q.worldedit.util.command.CommandCompleter;
|
||||
import com.sk89q.worldedit.util.command.Dispatcher;
|
||||
import com.sk89q.worldedit.util.command.NullCompleter;
|
||||
import com.sk89q.worldedit.util.command.binding.PrimitiveBindings;
|
||||
import com.sk89q.worldedit.util.command.binding.StandardBindings;
|
||||
import com.sk89q.worldedit.util.command.binding.Switch;
|
||||
import com.thoughtworks.paranamer.CachingParanamer;
|
||||
import com.thoughtworks.paranamer.Paranamer;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Creates commands using annotations placed on methods and individual parameters of
|
||||
* such methods.
|
||||
*
|
||||
* @see Command defines a command
|
||||
* @see Switch defines a flag
|
||||
*/
|
||||
public class ParametricBuilder {
|
||||
|
||||
private final Map<Type, Binding> bindings = new HashMap<Type, Binding>();
|
||||
private final Paranamer paranamer = new CachingParanamer();
|
||||
private final List<InvokeListener> invokeListeners = new ArrayList<InvokeListener>();
|
||||
private final List<ExceptionConverter> exceptionConverters = new ArrayList<ExceptionConverter>();
|
||||
private Authorizer authorizer = new NullAuthorizer();
|
||||
private CommandCompleter defaultCompleter = new NullCompleter();
|
||||
|
||||
/**
|
||||
* Create a new builder.
|
||||
*
|
||||
* <p>This method will install {@link PrimitiveBindings} and
|
||||
* {@link StandardBindings} and default bindings.</p>
|
||||
*/
|
||||
public ParametricBuilder() {
|
||||
addBinding(new PrimitiveBindings());
|
||||
addBinding(new StandardBindings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a binding for a given type or classifier (annotation).
|
||||
*
|
||||
* <p>Whenever a method parameter is encountered, a binding must be found for it
|
||||
* so that it can be called later to consume the stack of arguments provided by
|
||||
* the user and return an object that is later passed to
|
||||
* {@link Method#invoke(Object, Object...)}.</p>
|
||||
*
|
||||
* <p>Normally, a {@link Type} is used to discern between different bindings, but
|
||||
* if this is not specific enough, an annotation can be defined and used. This
|
||||
* makes it a "classifier" and it will take precedence over the base type. For
|
||||
* example, even if there is a binding that handles {@link String} parameters,
|
||||
* a special {@code @MyArg} annotation can be assigned to a {@link String}
|
||||
* parameter, which will cause the {@link Builder} to consult the {@link Binding}
|
||||
* associated with {@code @MyArg} rather than with the binding for
|
||||
* the {@link String} type.</p>
|
||||
*
|
||||
* @param binding the binding
|
||||
* @param type a list of types (if specified) to override the binding's types
|
||||
*/
|
||||
public void addBinding(Binding binding, Type... type) {
|
||||
if (type == null || type.length == 0) {
|
||||
type = binding.getTypes();
|
||||
}
|
||||
|
||||
for (Type t : type) {
|
||||
bindings.put(t, binding);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach an invocation listener.
|
||||
*
|
||||
* <p>Invocation handlers are called in order that their listeners are
|
||||
* registered with a {@link ParametricBuilder}. It is not guaranteed that
|
||||
* a listener may be called, in the case of a {@link CommandException} being
|
||||
* thrown at any time before the appropriate listener or handler is called.
|
||||
* It is possible for a
|
||||
* {@link InvokeHandler#preInvoke(Object, Method, ParameterData[], Object[], CommandContext)} to
|
||||
* be called for a invocation handler, but not the associated
|
||||
* {@link InvokeHandler#postInvoke(Object, Method, ParameterData[], Object[], CommandContext)}.</p>
|
||||
*
|
||||
* <p>An example of an invocation listener is one to handle
|
||||
* {@link CommandPermissions}, by first checking to see if permission is available
|
||||
* in a {@link InvokeHandler#preInvoke(Object, Method, ParameterData[], Object[], CommandContext)}
|
||||
* call. If permission is not found, then an appropriate {@link CommandException}
|
||||
* can be thrown to cease invocation.</p>
|
||||
*
|
||||
* @param listener the listener
|
||||
* @see InvokeHandler the handler
|
||||
*/
|
||||
public void addInvokeListener(InvokeListener listener) {
|
||||
invokeListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach an exception converter to this builder in order to wrap unknown
|
||||
* {@link Throwable}s into known {@link CommandException}s.
|
||||
*
|
||||
* <p>Exception converters are called in order that they are registered.</p>
|
||||
*
|
||||
* @param converter the converter
|
||||
* @see ExceptionConverter for an explanation
|
||||
*/
|
||||
public void addExceptionConverter(ExceptionConverter converter) {
|
||||
exceptionConverters.add(converter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of commands from methods specially annotated with {@link Command}
|
||||
* (and other relevant annotations) and register them all with the given
|
||||
* {@link Dispatcher}.
|
||||
*
|
||||
* @param dispatcher the dispatcher to register commands with
|
||||
* @param object the object contain the methods
|
||||
* @throws ParametricException thrown if the commands cannot be registered
|
||||
*/
|
||||
public void registerMethodsAsCommands(Dispatcher dispatcher, Object object) throws ParametricException {
|
||||
for (Method method : object.getClass().getDeclaredMethods()) {
|
||||
Command definition = method.getAnnotation(Command.class);
|
||||
if (definition != null) {
|
||||
CommandCallable callable = build(object, method, definition);
|
||||
dispatcher.registerCommand(callable, definition.aliases());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link CommandCallable} for the given method.
|
||||
*
|
||||
* @param object the object to be invoked on
|
||||
* @param method the method to invoke
|
||||
* @param definition the command definition annotation
|
||||
* @return the command executor
|
||||
* @throws ParametricException thrown on an error
|
||||
*/
|
||||
private CommandCallable build(Object object, Method method, Command definition)
|
||||
throws ParametricException {
|
||||
return new ParametricCallable(this, object, method, definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object used to get method names on Java versions before 8 (assuming
|
||||
* that Java 8 is given the ability to reliably reflect method names at runtime).
|
||||
*
|
||||
* @return the paranamer
|
||||
*/
|
||||
Paranamer getParanamer() {
|
||||
return paranamer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of bindings.
|
||||
*
|
||||
* @return the map of bindings
|
||||
*/
|
||||
Map<Type, Binding> getBindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of invocation listeners.
|
||||
*
|
||||
* @return a list of invocation listeners
|
||||
*/
|
||||
List<InvokeListener> getInvokeListeners() {
|
||||
return invokeListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of exception converters.
|
||||
*
|
||||
* @return a list of exception converters
|
||||
*/
|
||||
List<ExceptionConverter> getExceptionConverters() {
|
||||
return exceptionConverters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authorizer.
|
||||
*
|
||||
* @return the authorizer
|
||||
*/
|
||||
public Authorizer getAuthorizer() {
|
||||
return authorizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the authorizer.
|
||||
*
|
||||
* @param authorizer the authorizer
|
||||
*/
|
||||
public void setAuthorizer(Authorizer authorizer) {
|
||||
checkNotNull(authorizer);
|
||||
this.authorizer = authorizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default command suggestions provider that will be used if
|
||||
* no suggestions are available.
|
||||
*
|
||||
* @return the default command completer
|
||||
*/
|
||||
public CommandCompleter getDefaultCompleter() {
|
||||
return defaultCompleter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default command suggestions provider that will be used if
|
||||
* no suggestions are available.
|
||||
*
|
||||
* @param defaultCompleter the default command completer
|
||||
*/
|
||||
public void setDefaultCompleter(CommandCompleter defaultCompleter) {
|
||||
checkNotNull(defaultCompleter);
|
||||
this.defaultCompleter = defaultCompleter;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,483 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.google.common.primitives.Chars;
|
||||
import com.sk89q.minecraft.util.commands.Command;
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
|
||||
import com.sk89q.minecraft.util.commands.WrappedCommandException;
|
||||
import com.sk89q.worldedit.util.command.CommandCallable;
|
||||
import com.sk89q.worldedit.util.command.InvalidUsageException;
|
||||
import com.sk89q.worldedit.util.command.MissingParameterException;
|
||||
import com.sk89q.worldedit.util.command.Parameter;
|
||||
import com.sk89q.worldedit.util.command.SimpleDescription;
|
||||
import com.sk89q.worldedit.util.command.UnconsumedParameterException;
|
||||
import com.sk89q.worldedit.util.command.binding.Switch;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The implementation of a {@link CommandCallable} for the {@link ParametricBuilder}.
|
||||
*/
|
||||
class ParametricCallable implements CommandCallable {
|
||||
|
||||
private final ParametricBuilder builder;
|
||||
private final Object object;
|
||||
private final Method method;
|
||||
private final ParameterData[] parameters;
|
||||
private final Set<Character> valueFlags = new HashSet<Character>();
|
||||
private final boolean anyFlags;
|
||||
private final Set<Character> legacyFlags = new HashSet<Character>();
|
||||
private final SimpleDescription description = new SimpleDescription();
|
||||
private final CommandPermissions commandPermissions;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param builder the parametric builder
|
||||
* @param object the object to invoke on
|
||||
* @param method the method to invoke
|
||||
* @param definition the command definition annotation
|
||||
* @throws ParametricException thrown on an error
|
||||
*/
|
||||
ParametricCallable(ParametricBuilder builder, Object object, Method method, Command definition) throws ParametricException {
|
||||
this.builder = builder;
|
||||
this.object = object;
|
||||
this.method = method;
|
||||
|
||||
Annotation[][] annotations = method.getParameterAnnotations();
|
||||
String[] names = builder.getParanamer().lookupParameterNames(method, false);
|
||||
Type[] types = method.getGenericParameterTypes();
|
||||
parameters = new ParameterData[types.length];
|
||||
List<Parameter> userParameters = new ArrayList<Parameter>();
|
||||
|
||||
// This helps keep tracks of @Nullables that appear in the middle of a list
|
||||
// of parameters
|
||||
int numOptional = 0;
|
||||
|
||||
// Set permission hint
|
||||
CommandPermissions permHint = method.getAnnotation(CommandPermissions.class);
|
||||
if (permHint != null) {
|
||||
description.setPermissions(Arrays.asList(permHint.value()));
|
||||
}
|
||||
|
||||
// Go through each parameter
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
Type type = types[i];
|
||||
|
||||
ParameterData parameter = new ParameterData();
|
||||
parameter.setType(type);
|
||||
parameter.setModifiers(annotations[i]);
|
||||
|
||||
// Search for annotations
|
||||
for (Annotation annotation : annotations[i]) {
|
||||
if (annotation instanceof Switch) {
|
||||
parameter.setFlag(((Switch) annotation).value(), type != boolean.class);
|
||||
} else if (annotation instanceof Optional) {
|
||||
parameter.setOptional(true);
|
||||
String[] value = ((Optional) annotation).value();
|
||||
if (value.length > 0) {
|
||||
parameter.setDefaultValue(value);
|
||||
}
|
||||
// Special annotation bindings
|
||||
} else if (parameter.getBinding() == null) {
|
||||
parameter.setBinding(builder.getBindings().get(annotation.annotationType()));
|
||||
parameter.setClassifier(annotation);
|
||||
}
|
||||
}
|
||||
|
||||
parameter.setName(names.length > 0 ? names[i] : generateName(type, parameter.getClassifier(), i));
|
||||
|
||||
// Track all value flags
|
||||
if (parameter.isValueFlag()) {
|
||||
valueFlags.add(parameter.getFlag());
|
||||
}
|
||||
|
||||
// No special @annotation binding... let's check for the type
|
||||
if (parameter.getBinding() == null) {
|
||||
parameter.setBinding(builder.getBindings().get(type));
|
||||
|
||||
// Don't know how to parse for this type of value
|
||||
if (parameter.getBinding() == null) {
|
||||
throw new ParametricException("Don't know how to handle the parameter type '" + type + "' in\n" + method.toGenericString());
|
||||
}
|
||||
}
|
||||
|
||||
// Do some validation of this parameter
|
||||
parameter.validate(method, i + 1);
|
||||
|
||||
// Keep track of optional parameters
|
||||
if (parameter.isOptional() && parameter.getFlag() == null) {
|
||||
numOptional++;
|
||||
} else {
|
||||
if (numOptional > 0 && parameter.isNonFlagConsumer()) {
|
||||
if (parameter.getConsumedCount() < 0) {
|
||||
throw new ParametricException(
|
||||
"Found an parameter using the binding " +
|
||||
parameter.getBinding().getClass().getCanonicalName() +
|
||||
"\nthat does not know how many arguments it consumes, but " +
|
||||
"it follows an optional parameter\nMethod: " +
|
||||
method.toGenericString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameters[i] = parameter;
|
||||
|
||||
// Make a list of "real" parameters
|
||||
if (parameter.isUserInput()) {
|
||||
userParameters.add(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
// Gather legacy flags
|
||||
anyFlags = definition.anyFlags();
|
||||
legacyFlags.addAll(Chars.asList(definition.flags().toCharArray()));
|
||||
|
||||
// Finish description
|
||||
description.setDescription(!definition.desc().isEmpty() ? definition.desc() : null);
|
||||
description.setHelp(!definition.help().isEmpty() ? definition.help() : null);
|
||||
description.overrideUsage(!definition.usage().isEmpty() ? definition.usage() : null);
|
||||
|
||||
for (InvokeListener listener : builder.getInvokeListeners()) {
|
||||
listener.updateDescription(object, method, parameters, description);
|
||||
}
|
||||
|
||||
// Set parameters
|
||||
description.setParameters(userParameters);
|
||||
|
||||
// Get permissions annotation
|
||||
commandPermissions = method.getAnnotation(CommandPermissions.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean call(String stringArguments, CommandLocals locals, String[] parentCommands) throws CommandException {
|
||||
// Test permission
|
||||
if (!testPermission(locals)) {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
|
||||
String calledCommand = parentCommands.length > 0 ? parentCommands[parentCommands.length - 1] : "_";
|
||||
String[] split = CommandContext.split(calledCommand + " " + stringArguments);
|
||||
CommandContext context = new CommandContext(split, getValueFlags(), false, locals);
|
||||
|
||||
// Provide help if -? is specified
|
||||
if (context.hasFlag('?')) {
|
||||
throw new InvalidUsageException(null, this, true);
|
||||
}
|
||||
|
||||
Object[] args = new Object[parameters.length];
|
||||
ContextArgumentStack arguments = new ContextArgumentStack(context);
|
||||
ParameterData parameter = null;
|
||||
|
||||
try {
|
||||
// preProcess handlers
|
||||
List<InvokeHandler> handlers = new ArrayList<InvokeHandler>();
|
||||
for (InvokeListener listener : builder.getInvokeListeners()) {
|
||||
InvokeHandler handler = listener.createInvokeHandler();
|
||||
handlers.add(handler);
|
||||
handler.preProcess(object, method, parameters, context);
|
||||
}
|
||||
|
||||
// Collect parameters
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
parameter = parameters[i];
|
||||
|
||||
if (mayConsumeArguments(i, arguments)) {
|
||||
// Parse the user input into a method argument
|
||||
ArgumentStack usedArguments = getScopedContext(parameter, arguments);
|
||||
|
||||
try {
|
||||
args[i] = parameter.getBinding().bind(parameter, usedArguments, false);
|
||||
} catch (MissingParameterException e) {
|
||||
// Not optional? Then we can't execute this command
|
||||
if (!parameter.isOptional()) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
args[i] = getDefaultValue(i, arguments);
|
||||
}
|
||||
} else {
|
||||
args[i] = getDefaultValue(i, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for unused arguments
|
||||
checkUnconsumed(arguments);
|
||||
|
||||
// preInvoke handlers
|
||||
for (InvokeHandler handler : handlers) {
|
||||
handler.preInvoke(object, method, parameters, args, context);
|
||||
}
|
||||
|
||||
// Execute!
|
||||
method.invoke(object, args);
|
||||
|
||||
// postInvoke handlers
|
||||
for (InvokeHandler handler : handlers) {
|
||||
handler.postInvoke(handler, method, parameters, args, context);
|
||||
}
|
||||
} catch (MissingParameterException e) {
|
||||
throw new InvalidUsageException("Too few parameters!", this);
|
||||
} catch (UnconsumedParameterException e) {
|
||||
throw new InvalidUsageException("Too many parameters! Unused parameters: " + e.getUnconsumed(), this);
|
||||
} catch (ParameterException e) {
|
||||
assert parameter != null;
|
||||
String name = parameter.getName();
|
||||
|
||||
throw new InvalidUsageException("For parameter '" + name + "': " + e.getMessage(), this);
|
||||
} catch (InvocationTargetException e) {
|
||||
for (ExceptionConverter converter : builder.getExceptionConverters()) {
|
||||
converter.convert(e.getCause());
|
||||
}
|
||||
throw new WrappedCommandException(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WrappedCommandException(e);
|
||||
} catch (CommandException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new WrappedCommandException(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSuggestions(String arguments, CommandLocals locals) throws CommandException {
|
||||
return builder.getDefaultCompleter().getSuggestions(arguments, locals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of value flags used by this command.
|
||||
*
|
||||
* @return a list of value flags
|
||||
*/
|
||||
public Set<Character> getValueFlags() {
|
||||
return valueFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleDescription getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testPermission(CommandLocals locals) {
|
||||
if (commandPermissions != null) {
|
||||
for (String perm : commandPermissions.value()) {
|
||||
if (builder.getAuthorizer().testPermission(locals, perm)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the right {@link ArgumentStack}.
|
||||
*
|
||||
* @param parameter the parameter
|
||||
* @param existing the existing scoped context
|
||||
* @return the context to use
|
||||
*/
|
||||
private static ArgumentStack getScopedContext(Parameter parameter, ArgumentStack existing) {
|
||||
if (parameter.getFlag() != null) {
|
||||
CommandContext context = existing.getContext();
|
||||
|
||||
if (parameter.isValueFlag()) {
|
||||
return new StringArgumentStack(context, context.getFlag(parameter.getFlag()), false);
|
||||
} else {
|
||||
String v = context.hasFlag(parameter.getFlag()) ? "true" : "false";
|
||||
return new StringArgumentStack(context, v, true);
|
||||
}
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a parameter is allowed to consume arguments.
|
||||
*
|
||||
* @param i the index of the parameter
|
||||
* @param scoped the scoped context
|
||||
* @return true if arguments may be consumed
|
||||
*/
|
||||
private boolean mayConsumeArguments(int i, ContextArgumentStack scoped) {
|
||||
CommandContext context = scoped.getContext();
|
||||
ParameterData parameter = parameters[i];
|
||||
|
||||
// Flag parameters: Always consume
|
||||
// Required non-flag parameters: Always consume
|
||||
// Optional non-flag parameters:
|
||||
// - Before required parameters: Consume if there are 'left over' args
|
||||
// - At the end: Always consumes
|
||||
|
||||
if (parameter.isOptional()) {
|
||||
if (parameter.getFlag() != null) {
|
||||
return !parameter.isValueFlag() || context.hasFlag(parameter.getFlag());
|
||||
} else {
|
||||
int numberFree = context.argsLength() - scoped.position();
|
||||
for (int j = i; j < parameters.length; j++) {
|
||||
if (parameters[j].isNonFlagConsumer() && !parameters[j].isOptional()) {
|
||||
// We already checked if the consumed count was > -1
|
||||
// when we created this object
|
||||
numberFree -= parameters[j].getConsumedCount();
|
||||
}
|
||||
}
|
||||
|
||||
// Skip this optional parameter
|
||||
if (numberFree < 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default value for a parameter.
|
||||
*
|
||||
* @param i the index of the parameter
|
||||
* @param scoped the scoped context
|
||||
* @return a value
|
||||
* @throws ParameterException on an error
|
||||
* @throws CommandException on an error
|
||||
*/
|
||||
private Object getDefaultValue(int i, ContextArgumentStack scoped) throws ParameterException, CommandException, InvocationTargetException {
|
||||
CommandContext context = scoped.getContext();
|
||||
ParameterData parameter = parameters[i];
|
||||
|
||||
String[] defaultValue = parameter.getDefaultValue();
|
||||
if (defaultValue != null) {
|
||||
try {
|
||||
return parameter.getBinding().bind(parameter, new StringArgumentStack(context, defaultValue, false), false);
|
||||
} catch (MissingParameterException e) {
|
||||
throw new ParametricException(
|
||||
"The default value of the parameter using the binding " +
|
||||
parameter.getBinding().getClass() + " in the method\n" +
|
||||
method.toGenericString() + "\nis invalid");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check to see if all arguments, including flag arguments, were consumed.
|
||||
*
|
||||
* @param scoped the argument scope
|
||||
* @throws UnconsumedParameterException thrown if parameters were not consumed
|
||||
*/
|
||||
private void checkUnconsumed(ContextArgumentStack scoped) throws UnconsumedParameterException {
|
||||
CommandContext context = scoped.getContext();
|
||||
String unconsumed;
|
||||
String unconsumedFlags = getUnusedFlags(context);
|
||||
|
||||
if ((unconsumed = scoped.getUnconsumed()) != null) {
|
||||
throw new UnconsumedParameterException(unconsumed + " " + unconsumedFlags);
|
||||
}
|
||||
|
||||
if (unconsumedFlags != null) {
|
||||
throw new UnconsumedParameterException(unconsumedFlags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any unused flag arguments.
|
||||
*
|
||||
* @param context the command context
|
||||
*/
|
||||
private String getUnusedFlags(CommandContext context) {
|
||||
if (!anyFlags) {
|
||||
Set<Character> unusedFlags = null;
|
||||
for (char flag : context.getFlags()) {
|
||||
boolean found = false;
|
||||
|
||||
if (legacyFlags.contains(flag)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (ParameterData parameter : parameters) {
|
||||
Character paramFlag = parameter.getFlag();
|
||||
if (paramFlag != null && flag == paramFlag) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
if (unusedFlags == null) {
|
||||
unusedFlags = new HashSet<Character>();
|
||||
}
|
||||
unusedFlags.add(flag);
|
||||
}
|
||||
}
|
||||
|
||||
if (unusedFlags != null) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Character flag : unusedFlags) {
|
||||
builder.append("-").append(flag).append(" ");
|
||||
}
|
||||
|
||||
return builder.toString().trim();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a name for a parameter.
|
||||
*
|
||||
* @param type the type
|
||||
* @param classifier the classifier
|
||||
* @param index the index
|
||||
* @return a generated name
|
||||
*/
|
||||
private static String generateName(Type type, Annotation classifier, int index) {
|
||||
if (classifier != null) {
|
||||
return classifier.annotationType().getSimpleName().toLowerCase();
|
||||
} else {
|
||||
if (type instanceof Class<?>) {
|
||||
return ((Class<?>) type).getSimpleName().toLowerCase();
|
||||
} else {
|
||||
return "unknown" + index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
/**
|
||||
* Thrown if the {@link ParametricBuilder} can't build commands from
|
||||
* an object for whatever reason.
|
||||
*/
|
||||
public class ParametricException extends RuntimeException {
|
||||
|
||||
public ParametricException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ParametricException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ParametricException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ParametricException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.command.parametric;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.worldedit.util.command.MissingParameterException;
|
||||
import com.sk89q.util.StringUtil;
|
||||
|
||||
/**
|
||||
* A virtual scope that does not actually read from the underlying
|
||||
* {@link CommandContext}.
|
||||
*/
|
||||
public class StringArgumentStack implements ArgumentStack {
|
||||
|
||||
private final boolean nonNullBoolean;
|
||||
private final CommandContext context;
|
||||
private final String[] arguments;
|
||||
private int index = 0;
|
||||
|
||||
/**
|
||||
* Create a new instance using the given context.
|
||||
*
|
||||
* @param context the context
|
||||
* @param arguments a list of arguments
|
||||
* @param nonNullBoolean true to have {@link #nextBoolean()} return false instead of null
|
||||
*/
|
||||
public StringArgumentStack(
|
||||
CommandContext context, String[] arguments, boolean nonNullBoolean) {
|
||||
this.context = context;
|
||||
this.arguments = arguments;
|
||||
this.nonNullBoolean = nonNullBoolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the given context.
|
||||
*
|
||||
* @param context the context
|
||||
* @param arguments an argument string to be parsed
|
||||
* @param nonNullBoolean true to have {@link #nextBoolean()} return false instead of null
|
||||
*/
|
||||
public StringArgumentStack(
|
||||
CommandContext context, String arguments, boolean nonNullBoolean) {
|
||||
this.context = context;
|
||||
this.arguments = CommandContext.split(arguments);
|
||||
this.nonNullBoolean = nonNullBoolean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() throws ParameterException {
|
||||
try {
|
||||
return arguments[index++];
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new MissingParameterException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer nextInt() throws ParameterException {
|
||||
try {
|
||||
return Integer.parseInt(next());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParameterException(
|
||||
"Expected a number, got '" + context.getString(index - 1) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double nextDouble() throws ParameterException {
|
||||
try {
|
||||
return Double.parseDouble(next());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParameterException(
|
||||
"Expected a number, got '" + context.getString(index - 1) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean nextBoolean() throws ParameterException {
|
||||
try {
|
||||
return next().equalsIgnoreCase("true");
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
if (nonNullBoolean) { // Special case
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new MissingParameterException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remaining() throws ParameterException {
|
||||
try {
|
||||
String value = StringUtil.joinString(arguments, " ", index);
|
||||
markConsumed();
|
||||
return value;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new MissingParameterException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markConsumed() {
|
||||
index = arguments.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.concurrency;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Even more {@code ExecutorService} factory methods.
|
||||
*/
|
||||
public final class EvenMoreExecutors {
|
||||
|
||||
private EvenMoreExecutors() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a thread pool that creates new threads as needed up to
|
||||
* a maximum number of threads, but will reuse previously constructed
|
||||
* threads when they are available.
|
||||
*
|
||||
* @param minThreads the minimum number of threads to have at a given time
|
||||
* @param maxThreads the maximum number of threads to have at a given time
|
||||
* @param queueSize the size of the queue before new submissions are rejected
|
||||
* @return the newly created thread pool
|
||||
*/
|
||||
public static ExecutorService newBoundedCachedThreadPool(int minThreads, int maxThreads, int queueSize) {
|
||||
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
|
||||
minThreads, maxThreads,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new ArrayBlockingQueue<Runnable>(queueSize));
|
||||
threadPoolExecutor.allowCoreThreadTimeOut(true);
|
||||
return threadPoolExecutor;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.eventbus;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A {@link SubscriberFindingStrategy} for collecting all event handler methods
|
||||
* that are marked with the {@link Subscribe} annotation.
|
||||
*
|
||||
* <p>Original for Guava, licensed under the Apache License, Version 2.0.</p>
|
||||
*/
|
||||
class AnnotatedSubscriberFinder implements SubscriberFindingStrategy {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation finds all methods marked with a {@link Subscribe}
|
||||
* annotation.
|
||||
*/
|
||||
@Override
|
||||
public Multimap<Class<?>, EventHandler> findAllSubscribers(Object listener) {
|
||||
Multimap<Class<?>, EventHandler> methodsInListener = HashMultimap.create();
|
||||
Class<?> clazz = listener.getClass();
|
||||
while (clazz != null) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
Subscribe annotation = method.getAnnotation(Subscribe.class);
|
||||
method.setAccessible(true);
|
||||
|
||||
if (annotation != null) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Method " + method + " has @Subscribe annotation, but requires " +
|
||||
parameterTypes.length + " arguments. Event handler methods " +
|
||||
"must require a single argument.");
|
||||
}
|
||||
Class<?> eventType = parameterTypes[0];
|
||||
EventHandler handler = new MethodEventHandler(annotation.priority(), listener, method);
|
||||
methodsInListener.put(eventType, handler);
|
||||
}
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
|
||||
return methodsInListener;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.eventbus;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.eventbus.DeadEvent;
|
||||
import com.sk89q.worldedit.internal.annotation.RequiresNewerGuava;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Dispatches events to listeners, and provides ways for listeners to register
|
||||
* themselves.
|
||||
*
|
||||
* <p>This class is based on Guava's {@link EventBus} but priority is supported
|
||||
* and events are dispatched at the time of call, rather than being queued up.
|
||||
* This does allow dispatching during an in-progress dispatch.</p>
|
||||
*
|
||||
* <p>This implementation utilizes naive synchronization on all getter and
|
||||
* setter methods. Dispatch does not occur when a lock has been acquired,
|
||||
* however.</p>
|
||||
*/
|
||||
public class EventBus {
|
||||
|
||||
private final Logger logger = Logger.getLogger(EventBus.class.getCanonicalName());
|
||||
|
||||
private final SetMultimap<Class<?>, EventHandler> handlersByType =
|
||||
Multimaps.newSetMultimap(new HashMap<Class<?>, Collection<EventHandler>>(),
|
||||
new Supplier<Set<EventHandler>>() {
|
||||
@Override
|
||||
public Set<EventHandler> get() {
|
||||
return newHandlerSet();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Strategy for finding handler methods in registered objects. Currently,
|
||||
* only the {@link AnnotatedSubscriberFinder} is supported, but this is
|
||||
* encapsulated for future expansion.
|
||||
*/
|
||||
private final SubscriberFindingStrategy finder = new AnnotatedSubscriberFinder();
|
||||
|
||||
@RequiresNewerGuava
|
||||
private HierarchyCache flattenHierarchyCache = new HierarchyCache();
|
||||
|
||||
/**
|
||||
* Registers the given handler for the given class to receive events.
|
||||
*
|
||||
* @param clazz the event class to register
|
||||
* @param handler the handler to register
|
||||
*/
|
||||
public synchronized void subscribe(Class<?> clazz, EventHandler handler) {
|
||||
checkNotNull(clazz);
|
||||
checkNotNull(handler);
|
||||
handlersByType.put(clazz, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given handler for the given class to receive events.
|
||||
*
|
||||
* @param handlers a map of handlers
|
||||
*/
|
||||
public synchronized void subscribeAll(Multimap<Class<?>, EventHandler> handlers) {
|
||||
checkNotNull(handlers);
|
||||
handlersByType.putAll(handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given handler for the given class.
|
||||
*
|
||||
* @param clazz the class
|
||||
* @param handler the handler
|
||||
*/
|
||||
public synchronized void unsubscribe(Class<?> clazz, EventHandler handler) {
|
||||
checkNotNull(clazz);
|
||||
checkNotNull(handler);
|
||||
handlersByType.remove(clazz, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given handlers.
|
||||
*
|
||||
* @param handlers a map of handlers
|
||||
*/
|
||||
public synchronized void unsubscribeAll(Multimap<Class<?>, EventHandler> handlers) {
|
||||
checkNotNull(handlers);
|
||||
for (Map.Entry<Class<?>, Collection<EventHandler>> entry : handlers.asMap().entrySet()) {
|
||||
Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey());
|
||||
Collection<EventHandler> eventMethodsInListener = entry.getValue();
|
||||
|
||||
if (currentHandlers != null &&!currentHandlers.containsAll(entry.getValue())) {
|
||||
currentHandlers.removeAll(eventMethodsInListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all handler methods on {@code object} to receive events.
|
||||
* Handler methods are selected and classified using this EventBus's
|
||||
* {@link SubscriberFindingStrategy}; the default strategy is the
|
||||
* {@link AnnotatedSubscriberFinder}.
|
||||
*
|
||||
* @param object object whose handler methods should be registered.
|
||||
*/
|
||||
public void register(Object object) {
|
||||
subscribeAll(finder.findAllSubscribers(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters all handler methods on a registered {@code object}.
|
||||
*
|
||||
* @param object object whose handler methods should be unregistered.
|
||||
* @throws IllegalArgumentException if the object was not previously registered.
|
||||
*/
|
||||
public void unregister(Object object) {
|
||||
unsubscribeAll(finder.findAllSubscribers(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts an event to all registered handlers. This method will return
|
||||
* successfully after the event has been posted to all handlers, and
|
||||
* regardless of any exceptions thrown by handlers.
|
||||
*
|
||||
* <p>If no handlers have been subscribed for {@code event}'s class, and
|
||||
* {@code event} is not already a {@link DeadEvent}, it will be wrapped in a
|
||||
* DeadEvent and reposted.
|
||||
*
|
||||
* @param event event to post.
|
||||
*/
|
||||
public void post(Object event) {
|
||||
List<EventHandler> dispatching = new ArrayList<EventHandler>();
|
||||
|
||||
synchronized (this) {
|
||||
Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
|
||||
|
||||
for (Class<?> eventType : dispatchTypes) {
|
||||
Set<EventHandler> wrappers = getHandlersForEventType(eventType);
|
||||
|
||||
if (wrappers != null && !wrappers.isEmpty()) {
|
||||
dispatching.addAll(wrappers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(dispatching);
|
||||
|
||||
for (EventHandler handler : dispatching) {
|
||||
dispatch(event, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches {@code event} to the handler in {@code handler}. This method
|
||||
* is an appropriate override point for subclasses that wish to make
|
||||
* event delivery asynchronous.
|
||||
*
|
||||
* @param event event to dispatch.
|
||||
* @param handler handler that will call the handler.
|
||||
*/
|
||||
protected void dispatch(Object event, EventHandler handler) {
|
||||
try {
|
||||
handler.handleEvent(event);
|
||||
} catch (InvocationTargetException e) {
|
||||
logger.log(Level.SEVERE,
|
||||
"Could not dispatch event: " + event + " to handler " + handler, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a mutable set of the currently registered handlers for
|
||||
* {@code type}. If no handlers are currently registered for {@code type},
|
||||
* this method may either return {@code null} or an empty set.
|
||||
*
|
||||
* @param type type of handlers to retrieve.
|
||||
* @return currently registered handlers, or {@code null}.
|
||||
*/
|
||||
synchronized Set<EventHandler> getHandlersForEventType(Class<?> type) {
|
||||
return handlersByType.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Set for insertion into the handler map. This is provided
|
||||
* as an override point for subclasses. The returned set should support
|
||||
* concurrent access.
|
||||
*
|
||||
* @return a new, mutable set for handlers.
|
||||
*/
|
||||
protected synchronized Set<EventHandler> newHandlerSet() {
|
||||
return new HashSet<EventHandler>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens a class's type hierarchy into a set of Class objects. The set
|
||||
* will include all superclasses (transitively), and all interfaces
|
||||
* implemented by these superclasses.
|
||||
*
|
||||
* @param concreteClass class whose type hierarchy will be retrieved.
|
||||
* @return {@code clazz}'s complete type hierarchy, flattened and uniqued.
|
||||
*/
|
||||
Set<Class<?>> flattenHierarchy(Class<?> concreteClass) {
|
||||
return flattenHierarchyCache.get(concreteClass);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.eventbus;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Event handler object for {@link EventBus} that is able to dispatch
|
||||
* an event.
|
||||
*
|
||||
* <p>Original for Guava, licensed under the Apache License, Version 2.0.</p>
|
||||
*/
|
||||
public abstract class EventHandler implements Comparable<EventHandler> {
|
||||
|
||||
public enum Priority {
|
||||
VERY_EARLY,
|
||||
EARLY,
|
||||
NORMAL,
|
||||
LATE,
|
||||
VERY_LATE
|
||||
}
|
||||
|
||||
private final Priority priority;
|
||||
|
||||
/**
|
||||
* Create a new event handler.
|
||||
*
|
||||
* @param priority the priority
|
||||
*/
|
||||
protected EventHandler(Priority priority) {
|
||||
checkNotNull(priority);
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the priority.
|
||||
*
|
||||
* @return the priority
|
||||
*/
|
||||
public Priority getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the given event.
|
||||
*
|
||||
* <p>Subclasses should override {@link #dispatch(Object)}.</p>
|
||||
*
|
||||
* @param event the event
|
||||
* @throws InvocationTargetException thrown if an exception is thrown during dispatch
|
||||
*/
|
||||
public final void handleEvent(Object event) throws InvocationTargetException {
|
||||
try {
|
||||
dispatch(event);
|
||||
} catch (Throwable t) {
|
||||
throw new InvocationTargetException(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the event.
|
||||
*
|
||||
* @param event the event object
|
||||
* @throws Exception an exception that may be thrown
|
||||
*/
|
||||
public abstract void dispatch(Object event) throws Exception;
|
||||
|
||||
@Override
|
||||
public int compareTo(EventHandler o) {
|
||||
return getPriority().ordinal() - o.getPriority().ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract int hashCode();
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object obj);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EventHandler{" +
|
||||
"priority=" + priority +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.eventbus;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.sk89q.worldedit.internal.annotation.RequiresNewerGuava;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Holds a cache of class hierarchy.
|
||||
*
|
||||
* <p>This exists because Bukkit has an ancient version of Guava and the cache
|
||||
* library in Guava has since changed.</>
|
||||
*/
|
||||
@RequiresNewerGuava
|
||||
class HierarchyCache {
|
||||
|
||||
private final Map<Class<?>, Set<Class<?>>> cache = new WeakHashMap<Class<?>, Set<Class<?>>>();
|
||||
|
||||
public Set<Class<?>> get(Class<?> concreteClass) {
|
||||
Set<Class<?>> ret = cache.get(concreteClass);
|
||||
if (ret == null) {
|
||||
ret = build(concreteClass);
|
||||
cache.put(concreteClass, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected Set<Class<?>> build(Class<?> concreteClass) {
|
||||
List<Class<?>> parents = Lists.newLinkedList();
|
||||
Set<Class<?>> classes = Sets.newHashSet();
|
||||
|
||||
parents.add(concreteClass);
|
||||
|
||||
while (!parents.isEmpty()) {
|
||||
Class<?> clazz = parents.remove(0);
|
||||
classes.add(clazz);
|
||||
|
||||
Class<?> parent = clazz.getSuperclass();
|
||||
if (parent != null) {
|
||||
parents.add(parent);
|
||||
}
|
||||
|
||||
Collections.addAll(parents, clazz.getInterfaces());
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.eventbus;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Invokes a {@link Method} to dispatch an event.
|
||||
*/
|
||||
public class MethodEventHandler extends EventHandler {
|
||||
|
||||
private final Object object;
|
||||
private final Method method;
|
||||
|
||||
/**
|
||||
* Create a new event handler.
|
||||
*
|
||||
* @param priority the priority
|
||||
* @param method the method
|
||||
*/
|
||||
public MethodEventHandler(Priority priority, Object object, Method method) {
|
||||
super(priority);
|
||||
checkNotNull(method);
|
||||
this.object = object;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the method.
|
||||
*
|
||||
* @return the method
|
||||
*/
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(Object event) throws Exception {
|
||||
method.invoke(object, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
MethodEventHandler that = (MethodEventHandler) o;
|
||||
|
||||
if (!method.equals(that.method)) return false;
|
||||
if (object != null ? !object.equals(that.object) : that.object != null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = object != null ? object.hashCode() : 0;
|
||||
result = 31 * result + method.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.eventbus;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
|
||||
/**
|
||||
* Used to mark methods as event handlers.
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(METHOD)
|
||||
public @interface Subscribe {
|
||||
|
||||
/**
|
||||
* The priority as far as order of dispatching is concerned.
|
||||
*
|
||||
* @return the priority
|
||||
*/
|
||||
EventHandler.Priority priority() default EventHandler.Priority.NORMAL;
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.eventbus;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
/**
|
||||
* A method for finding event handler methods in objects, for use by
|
||||
* {@link EventBus}.
|
||||
*/
|
||||
interface SubscriberFindingStrategy {
|
||||
|
||||
/**
|
||||
* Finds all suitable event handler methods in {@code source}, organizes them
|
||||
* by the type of event they handle, and wraps them in {@link EventHandler}s.
|
||||
*
|
||||
* @param source object whose handlers are desired.
|
||||
* @return EventHandler objects for each handler method, organized by event
|
||||
* type.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code source} is not appropriate for
|
||||
* this strategy (in ways that this interface does not define).
|
||||
*/
|
||||
Multimap<Class<?>, EventHandler> findAllSubscribers(Object source);
|
||||
|
||||
}
|
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ColorCodeBuilder {
|
||||
|
||||
private static final ColorCodeBuilder instance = new ColorCodeBuilder();
|
||||
private static final Joiner newLineJoiner = Joiner.on("\n");
|
||||
public static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 47;
|
||||
|
||||
/**
|
||||
* Convert a message into color-coded text.
|
||||
*
|
||||
* @param message the message
|
||||
* @return a list of lines
|
||||
*/
|
||||
public String[] build(StyledFragment message) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
buildFragment(builder, message, message.getStyle(), new StyleSet());
|
||||
return builder.toString().split("\r?\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a fragment.
|
||||
*
|
||||
* @param builder the string builder
|
||||
* @param message the message
|
||||
* @param parentStyle the parent style
|
||||
* @param lastStyle the last style
|
||||
* @return the last style used
|
||||
*/
|
||||
private StyleSet buildFragment(StringBuilder builder, StyledFragment message, StyleSet parentStyle, StyleSet lastStyle) {
|
||||
for (Fragment node : message.getChildren()) {
|
||||
if (node instanceof StyledFragment) {
|
||||
StyledFragment fragment = (StyledFragment) node;
|
||||
lastStyle = buildFragment(
|
||||
builder, fragment,
|
||||
parentStyle.extend(message.getStyle()), lastStyle);
|
||||
} else {
|
||||
StyleSet style = parentStyle.extend(message.getStyle());
|
||||
builder.append(getAdditive(style, lastStyle));
|
||||
builder.append(node);
|
||||
lastStyle = style;
|
||||
}
|
||||
}
|
||||
|
||||
return lastStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatting codes.
|
||||
*
|
||||
* @param style the style
|
||||
* @return the color codes
|
||||
*/
|
||||
public static String getFormattingCode(StyleSet style) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (style.isBold()) {
|
||||
builder.append(Style.BOLD);
|
||||
}
|
||||
if (style.isItalic()) {
|
||||
builder.append(Style.ITALIC);
|
||||
}
|
||||
if (style.isUnderline()) {
|
||||
builder.append(Style.UNDERLINE);
|
||||
}
|
||||
if (style.isStrikethrough()) {
|
||||
builder.append(Style.STRIKETHROUGH);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatting and color codes.
|
||||
*
|
||||
* @param style the style
|
||||
* @return the color codes
|
||||
*/
|
||||
public static String getCode(StyleSet style) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getFormattingCode(style));
|
||||
if (style.getColor() != null) {
|
||||
builder.append(style.getColor());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the additional color codes needed to set the given style when the current
|
||||
* style is the other given one.
|
||||
*
|
||||
* @param resetTo the style to reset to
|
||||
* @param resetFrom the style to reset from
|
||||
* @return the color codes
|
||||
*/
|
||||
public static String getAdditive(StyleSet resetTo, StyleSet resetFrom) {
|
||||
if (!resetFrom.hasFormatting() && resetTo.hasFormatting()) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getFormattingCode(resetTo));
|
||||
if (resetFrom.getColor() != resetTo.getColor()) {
|
||||
builder.append(resetTo.getColor());
|
||||
}
|
||||
return builder.toString();
|
||||
} else if (!resetFrom.hasEqualFormatting(resetTo) ||
|
||||
(resetFrom.getColor() != null && resetTo.getColor() == null)) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
// Have to set reset code and add back all the formatting codes
|
||||
builder.append(Style.RESET);
|
||||
builder.append(getCode(resetTo));
|
||||
return builder.toString();
|
||||
} else {
|
||||
if (resetFrom.getColor() != resetTo.getColor()) {
|
||||
return String.valueOf(resetTo.getColor());
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Word wrap the given text and maintain color codes throughout lines.
|
||||
*
|
||||
* <p>This is borrowed from Bukkit.</p>
|
||||
*
|
||||
* @param rawString the raw string
|
||||
* @param lineLength the maximum line length
|
||||
* @return a list of lines
|
||||
*/
|
||||
private String[] wordWrap(String rawString, int lineLength) {
|
||||
// A null string is a single line
|
||||
if (rawString == null) {
|
||||
return new String[] {""};
|
||||
}
|
||||
|
||||
// A string shorter than the lineWidth is a single line
|
||||
if (rawString.length() <= lineLength && !rawString.contains("\n")) {
|
||||
return new String[] {rawString};
|
||||
}
|
||||
|
||||
char[] rawChars = (rawString + ' ').toCharArray(); // add a trailing space to trigger pagination
|
||||
StringBuilder word = new StringBuilder();
|
||||
StringBuilder line = new StringBuilder();
|
||||
List<String> lines = new LinkedList<String>();
|
||||
int lineColorChars = 0;
|
||||
|
||||
for (int i = 0; i < rawChars.length; i++) {
|
||||
char c = rawChars[i];
|
||||
|
||||
// skip chat color modifiers
|
||||
if (c == Style.COLOR_CHAR) {
|
||||
word.append(Style.getByChar(rawChars[i + 1]));
|
||||
lineColorChars += 2;
|
||||
i++; // Eat the next character as we have already processed it
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ' ' || c == '\n') {
|
||||
if (line.length() == 0 && word.length() > lineLength) { // special case: extremely long word begins a line
|
||||
String wordStr = word.toString();
|
||||
String transformed;
|
||||
if ((transformed = transform(wordStr)) != null) {
|
||||
line.append(transformed);
|
||||
} else {
|
||||
for (String partialWord : word.toString().split("(?<=\\G.{" + lineLength + "})")) {
|
||||
lines.add(partialWord);
|
||||
}
|
||||
}
|
||||
} else if (line.length() + word.length() - lineColorChars == lineLength) { // Line exactly the correct length...newline
|
||||
line.append(' ');
|
||||
line.append(word);
|
||||
lines.add(line.toString());
|
||||
line = new StringBuilder();
|
||||
lineColorChars = 0;
|
||||
} else if (line.length() + 1 + word.length() - lineColorChars > lineLength) { // Line too long...break the line
|
||||
String wordStr = word.toString();
|
||||
String transformed;
|
||||
if (word.length() > lineLength && (transformed = transform(wordStr)) != null) {
|
||||
if (line.length() + 1 + transformed.length() - lineColorChars > lineLength) {
|
||||
lines.add(line.toString());
|
||||
line = new StringBuilder(transformed);
|
||||
lineColorChars = 0;
|
||||
} else {
|
||||
if (line.length() > 0) {
|
||||
line.append(' ');
|
||||
}
|
||||
line.append(transformed);
|
||||
}
|
||||
} else {
|
||||
for (String partialWord : wordStr.split("(?<=\\G.{" + lineLength + "})")) {
|
||||
lines.add(line.toString());
|
||||
line = new StringBuilder(partialWord);
|
||||
}
|
||||
lineColorChars = 0;
|
||||
}
|
||||
} else {
|
||||
if (line.length() > 0) {
|
||||
line.append(' ');
|
||||
}
|
||||
line.append(word);
|
||||
}
|
||||
word = new StringBuilder();
|
||||
|
||||
if (c == '\n') { // Newline forces the line to flush
|
||||
lines.add(line.toString());
|
||||
line = new StringBuilder();
|
||||
}
|
||||
} else {
|
||||
word.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if(line.length() > 0) { // Only add the last line if there is anything to add
|
||||
lines.add(line.toString());
|
||||
}
|
||||
|
||||
// Iterate over the wrapped lines, applying the last color from one line to the beginning of the next
|
||||
if (lines.get(0).isEmpty() || lines.get(0).charAt(0) != Style.COLOR_CHAR) {
|
||||
lines.set(0, Style.WHITE + lines.get(0));
|
||||
}
|
||||
for (int i = 1; i < lines.size(); i++) {
|
||||
final String pLine = lines.get(i-1);
|
||||
final String subLine = lines.get(i);
|
||||
|
||||
char color = pLine.charAt(pLine.lastIndexOf(Style.COLOR_CHAR) + 1);
|
||||
if (subLine.isEmpty() || subLine.charAt(0) != Style.COLOR_CHAR) {
|
||||
lines.set(i, Style.getByChar(color) + subLine);
|
||||
}
|
||||
}
|
||||
|
||||
return lines.toArray(new String[lines.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for transforming a word, such as a URL.
|
||||
*
|
||||
* @param word the word
|
||||
* @return the transformed value, or null to do nothing
|
||||
*/
|
||||
protected String transform(String word) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given styled fragment into color codes.
|
||||
*
|
||||
* @param fragment the fragment
|
||||
* @return color codes
|
||||
*/
|
||||
public static String asColorCodes(StyledFragment fragment) {
|
||||
return newLineJoiner.join(instance.build(fragment));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting;
|
||||
|
||||
/**
|
||||
* A fragment of text.
|
||||
*/
|
||||
public class Fragment {
|
||||
|
||||
private final StringBuilder builder = new StringBuilder();
|
||||
|
||||
Fragment() {
|
||||
}
|
||||
|
||||
public Fragment append(String str) {
|
||||
builder.append(Style.stripColor(str));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(Object obj) {
|
||||
append(String.valueOf(obj));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(StringBuffer sb) {
|
||||
append(String.valueOf(sb));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(CharSequence s) {
|
||||
append(String.valueOf(s));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(boolean b) {
|
||||
append(String.valueOf(b));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(char c) {
|
||||
append(String.valueOf(c));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(int i) {
|
||||
append(String.valueOf(i));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(long lng) {
|
||||
append(String.valueOf(lng));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(float f) {
|
||||
append(String.valueOf(f));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment append(double d) {
|
||||
append(String.valueOf(d));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Fragment newLine() {
|
||||
append("\n");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* All supported color values for chat.
|
||||
*
|
||||
* <p>From Bukkit.</p>
|
||||
*/
|
||||
public enum Style {
|
||||
/**
|
||||
* Represents black
|
||||
*/
|
||||
BLACK('0', 0x00),
|
||||
/**
|
||||
* Represents dark blue
|
||||
*/
|
||||
BLUE_DARK('1', 0x1),
|
||||
/**
|
||||
* Represents dark green
|
||||
*/
|
||||
GREEN_DARK('2', 0x2),
|
||||
/**
|
||||
* Represents dark blue (aqua)
|
||||
*/
|
||||
CYAN_DARK('3', 0x3),
|
||||
/**
|
||||
* Represents dark red
|
||||
*/
|
||||
RED_DARK('4', 0x4),
|
||||
/**
|
||||
* Represents dark purple
|
||||
*/
|
||||
PURPLE_DARK('5', 0x5),
|
||||
/**
|
||||
* Represents gold
|
||||
*/
|
||||
YELLOW_DARK('6', 0x6),
|
||||
/**
|
||||
* Represents gray
|
||||
*/
|
||||
GRAY('7', 0x7),
|
||||
/**
|
||||
* Represents dark gray
|
||||
*/
|
||||
GRAY_DARK('8', 0x8),
|
||||
/**
|
||||
* Represents blue
|
||||
*/
|
||||
BLUE('9', 0x9),
|
||||
/**
|
||||
* Represents green
|
||||
*/
|
||||
GREEN('a', 0xA),
|
||||
/**
|
||||
* Represents aqua
|
||||
*/
|
||||
CYAN('b', 0xB),
|
||||
/**
|
||||
* Represents red
|
||||
*/
|
||||
RED('c', 0xC),
|
||||
/**
|
||||
* Represents light purple
|
||||
*/
|
||||
PURPLE('d', 0xD),
|
||||
/**
|
||||
* Represents yellow
|
||||
*/
|
||||
YELLOW('e', 0xE),
|
||||
/**
|
||||
* Represents white
|
||||
*/
|
||||
WHITE('f', 0xF),
|
||||
/**
|
||||
* Represents magical characters that change around randomly
|
||||
*/
|
||||
RANDOMIZE('k', 0x10, true),
|
||||
/**
|
||||
* Makes the text bold.
|
||||
*/
|
||||
BOLD('l', 0x11, true),
|
||||
/**
|
||||
* Makes a line appear through the text.
|
||||
*/
|
||||
STRIKETHROUGH('m', 0x12, true),
|
||||
/**
|
||||
* Makes the text appear underlined.
|
||||
*/
|
||||
UNDERLINE('n', 0x13, true),
|
||||
/**
|
||||
* Makes the text italic.
|
||||
*/
|
||||
ITALIC('o', 0x14, true),
|
||||
/**
|
||||
* Resets all previous chat colors or formats.
|
||||
*/
|
||||
RESET('r', 0x15);
|
||||
|
||||
/**
|
||||
* The special character which prefixes all chat color codes. Use this if you need to dynamically
|
||||
* convert color codes from your custom format.
|
||||
*/
|
||||
public static final char COLOR_CHAR = '\u00A7';
|
||||
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + COLOR_CHAR + "[0-9A-FK-OR]");
|
||||
|
||||
private final int intCode;
|
||||
private final char code;
|
||||
private final boolean isFormat;
|
||||
private final String toString;
|
||||
private final static Map<Integer, Style> BY_ID = Maps.newHashMap();
|
||||
private final static Map<Character, Style> BY_CHAR = Maps.newHashMap();
|
||||
|
||||
private Style(char code, int intCode) {
|
||||
this(code, intCode, false);
|
||||
}
|
||||
|
||||
private Style(char code, int intCode, boolean isFormat) {
|
||||
this.code = code;
|
||||
this.intCode = intCode;
|
||||
this.isFormat = isFormat;
|
||||
this.toString = new String(new char[] {COLOR_CHAR, code});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the char value associated with this color
|
||||
*
|
||||
* @return A char value of this color code
|
||||
*/
|
||||
public char getChar() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this code is a format code as opposed to a color code.
|
||||
*
|
||||
* @return the if the code is a formatting code
|
||||
*/
|
||||
public boolean isFormat() {
|
||||
return isFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this code is a color code as opposed to a format code.
|
||||
*
|
||||
* @return the if the code is a color
|
||||
*/
|
||||
public boolean isColor() {
|
||||
return !isFormat && this != RESET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color represented by the specified color code
|
||||
*
|
||||
* @param code Code to check
|
||||
* @return Associative {@link org.bukkit.ChatColor} with the given code, or null if it doesn't exist
|
||||
*/
|
||||
public static Style getByChar(char code) {
|
||||
return BY_CHAR.get(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color represented by the specified color code
|
||||
*
|
||||
* @param code Code to check
|
||||
* @return Associative {@link org.bukkit.ChatColor} with the given code, or null if it doesn't exist
|
||||
*/
|
||||
public static Style getByChar(String code) {
|
||||
checkNotNull(code);
|
||||
checkArgument(!code.isEmpty(), "Code must have at least one character");
|
||||
|
||||
return BY_CHAR.get(code.charAt(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the given message of all color codes
|
||||
*
|
||||
* @param input String to strip of color
|
||||
* @return A copy of the input string, without any coloring
|
||||
*/
|
||||
public static String stripColor(final String input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string using an alternate color code character into a string that uses the internal
|
||||
* ChatColor.COLOR_CODE color code character. The alternate color code character will only be replaced
|
||||
* if it is immediately followed by 0-9, A-F, a-f, K-O, k-o, R or r.
|
||||
*
|
||||
* @param altColorChar The alternate color code character to replace. Ex: &
|
||||
* @param textToTranslate Text containing the alternate color code character.
|
||||
* @return Text containing the ChatColor.COLOR_CODE color code character.
|
||||
*/
|
||||
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
for (int i = 0; i < b.length - 1; i++) {
|
||||
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i+1]) > -1) {
|
||||
b[i] = Style.COLOR_CHAR;
|
||||
b[i+1] = Character.toLowerCase(b[i+1]);
|
||||
}
|
||||
}
|
||||
return new String(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ChatColors used at the end of the given input string.
|
||||
*
|
||||
* @param input Input string to retrieve the colors from.
|
||||
* @return Any remaining ChatColors to pass onto the next line.
|
||||
*/
|
||||
public static String getLastColors(String input) {
|
||||
String result = "";
|
||||
int length = input.length();
|
||||
|
||||
// Search backwards from the end as it is faster
|
||||
for (int index = length - 1; index > -1; index--) {
|
||||
char section = input.charAt(index);
|
||||
if (section == COLOR_CHAR && index < length - 1) {
|
||||
char c = input.charAt(index + 1);
|
||||
Style color = getByChar(c);
|
||||
|
||||
if (color != null) {
|
||||
result = color + result;
|
||||
|
||||
// Once we find a color or reset we can stop searching
|
||||
if (color.isColor() || color == RESET) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static {
|
||||
for (Style color : values()) {
|
||||
BY_ID.put(color.intCode, color);
|
||||
BY_CHAR.put(color.code, color);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting;
|
||||
|
||||
/**
|
||||
* Represents set of styles, such as color, bold, etc.
|
||||
*/
|
||||
public class StyleSet {
|
||||
|
||||
private Boolean bold;
|
||||
private Boolean italic;
|
||||
private Boolean underline;
|
||||
private Boolean strikethrough;
|
||||
private Style color;
|
||||
|
||||
/**
|
||||
* Create a new style set with no properties set.
|
||||
*/
|
||||
public StyleSet() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new style set with the given styles.
|
||||
*
|
||||
* <p>{@link Style#RESET} will be ignored if provided.</p>
|
||||
*
|
||||
* @param styles a list of styles
|
||||
*/
|
||||
public StyleSet(Style... styles) {
|
||||
for (Style style : styles) {
|
||||
if (style.isColor()) {
|
||||
color = style;
|
||||
} else if (style == Style.BOLD) {
|
||||
bold = true;
|
||||
} else if (style == Style.ITALIC) {
|
||||
italic = true;
|
||||
} else if (style == Style.UNDERLINE) {
|
||||
underline = true;
|
||||
} else if (style == Style.STRIKETHROUGH) {
|
||||
strikethrough = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether this style set is bold.
|
||||
*
|
||||
* @return true, false, or null if unset
|
||||
*/
|
||||
public Boolean getBold() {
|
||||
return bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the text is bold.
|
||||
*
|
||||
* @return true if bold
|
||||
*/
|
||||
public boolean isBold() {
|
||||
return getBold() != null && getBold();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the text is bold.
|
||||
*
|
||||
* @param bold true, false, or null to unset
|
||||
*/
|
||||
public void setBold(Boolean bold) {
|
||||
this.bold = bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether this style set is italicized.
|
||||
*
|
||||
* @return true, false, or null if unset
|
||||
*/
|
||||
public Boolean getItalic() {
|
||||
return italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the text is italicized.
|
||||
*
|
||||
* @return true if italicized
|
||||
*/
|
||||
public boolean isItalic() {
|
||||
return getItalic() != null && getItalic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the text is italicized.
|
||||
*
|
||||
* @param italic false, or null to unset
|
||||
*/
|
||||
public void setItalic(Boolean italic) {
|
||||
this.italic = italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether this style set is underlined.
|
||||
*
|
||||
* @return true, false, or null if unset
|
||||
*/
|
||||
public Boolean getUnderline() {
|
||||
return underline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the text is underlined.
|
||||
*
|
||||
* @return true if underlined
|
||||
*/
|
||||
public boolean isUnderline() {
|
||||
return getUnderline() != null && getUnderline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the text is underline.
|
||||
*
|
||||
* @param underline false, or null to unset
|
||||
*/
|
||||
public void setUnderline(Boolean underline) {
|
||||
this.underline = underline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether this style set is stricken through.
|
||||
*
|
||||
* @return true, false, or null if unset
|
||||
*/
|
||||
public Boolean getStrikethrough() {
|
||||
return strikethrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the text is stricken through.
|
||||
*
|
||||
* @return true if there is strikethrough applied
|
||||
*/
|
||||
public boolean isStrikethrough() {
|
||||
return getStrikethrough() != null && getStrikethrough();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the text is stricken through.
|
||||
*
|
||||
* @param strikethrough false, or null to unset
|
||||
*/
|
||||
public void setStrikethrough(Boolean strikethrough) {
|
||||
this.strikethrough = strikethrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color of the text.
|
||||
*
|
||||
* @return true, false, or null if unset
|
||||
*/
|
||||
public Style getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color of the text.
|
||||
*
|
||||
* @param color the color
|
||||
*/
|
||||
public void setColor(Style color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether text formatting (bold, italics, underline, strikethrough) is set.
|
||||
*
|
||||
* @return true if formatting is set
|
||||
*/
|
||||
public boolean hasFormatting() {
|
||||
return getBold() != null || getItalic() != null
|
||||
|| getUnderline() != null || getStrikethrough() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return where the text formatting of the given style set is different from
|
||||
* that assigned to this one.
|
||||
*
|
||||
* @param other the other style set
|
||||
* @return true if there is a difference
|
||||
*/
|
||||
public boolean hasEqualFormatting(StyleSet other) {
|
||||
return getBold() == other.getBold() && getItalic() == other.getItalic()
|
||||
&& getUnderline() == other.getUnderline() &&
|
||||
getStrikethrough() == other.getStrikethrough();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with styles inherited from this one but with new styles
|
||||
* from the given style set.
|
||||
*
|
||||
* @param style the style set
|
||||
* @return a new style set instance
|
||||
*/
|
||||
public StyleSet extend(StyleSet style) {
|
||||
StyleSet newStyle = clone();
|
||||
if (style.getBold() != null) {
|
||||
newStyle.setBold(style.getBold());
|
||||
}
|
||||
if (style.getItalic() != null) {
|
||||
newStyle.setItalic(style.getItalic());
|
||||
}
|
||||
if (style.getUnderline() != null) {
|
||||
newStyle.setUnderline(style.getUnderline());
|
||||
}
|
||||
if (style.getStrikethrough() != null) {
|
||||
newStyle.setStrikethrough(style.getStrikethrough());
|
||||
}
|
||||
if (style.getColor() != null) {
|
||||
newStyle.setColor(style.getColor());
|
||||
}
|
||||
return newStyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyleSet clone() {
|
||||
StyleSet style = new StyleSet();
|
||||
style.setBold(getBold());
|
||||
style.setItalic(getItalic());
|
||||
style.setUnderline(getUnderline());
|
||||
style.setStrikethrough(getStrikethrough());
|
||||
style.setColor(getColor());
|
||||
return style;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A fragment of text that can be styled.
|
||||
*/
|
||||
public class StyledFragment extends Fragment {
|
||||
|
||||
private final List<Fragment> children = new ArrayList<Fragment>();
|
||||
private StyleSet style;
|
||||
private Fragment lastText;
|
||||
|
||||
public StyledFragment() {
|
||||
style = new StyleSet();
|
||||
}
|
||||
|
||||
public StyledFragment(StyleSet style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public StyledFragment(Style... styles) {
|
||||
this.style = new StyleSet(styles);
|
||||
}
|
||||
|
||||
public StyleSet getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
public void setStyles(StyleSet style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public List<Fragment> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
protected Fragment lastText() {
|
||||
Fragment text;
|
||||
if (!children.isEmpty()) {
|
||||
text = children.get(children.size() - 1);
|
||||
if (text == lastText) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
text = new Fragment();
|
||||
this.lastText = text;
|
||||
children.add(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
public StyledFragment createFragment(Style... styles) {
|
||||
StyledFragment fragment = new StyledFragment(styles);
|
||||
append(fragment);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public StyledFragment append(StyledFragment fragment) {
|
||||
children.add(fragment);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(String str) {
|
||||
lastText().append(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(Object obj) {
|
||||
append(String.valueOf(obj));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(StringBuffer sb) {
|
||||
append(String.valueOf(sb));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(CharSequence s) {
|
||||
append(String.valueOf(s));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(boolean b) {
|
||||
append(String.valueOf(b));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(char c) {
|
||||
append(String.valueOf(c));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(int i) {
|
||||
append(String.valueOf(i));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(long lng) {
|
||||
append(String.valueOf(lng));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(float f) {
|
||||
append(String.valueOf(f));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment append(double d) {
|
||||
append(String.valueOf(d));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyledFragment newLine() {
|
||||
append("\n");
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting.component;
|
||||
|
||||
import com.sk89q.worldedit.util.formatting.Style;
|
||||
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||
|
||||
/**
|
||||
* Represents a fragment representing a command that is to be typed.
|
||||
*/
|
||||
public class Code extends StyledFragment {
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
public Code() {
|
||||
super(Style.CYAN);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting.component;
|
||||
|
||||
import com.sk89q.worldedit.util.formatting.Style;
|
||||
|
||||
public class CommandListBox extends MessageBox {
|
||||
|
||||
private boolean first = true;
|
||||
|
||||
/**
|
||||
* Create a new box.
|
||||
*
|
||||
* @param title the title
|
||||
*/
|
||||
public CommandListBox(String title) {
|
||||
super(title);
|
||||
}
|
||||
|
||||
public CommandListBox appendCommand(String alias, String description) {
|
||||
if (!first) {
|
||||
getContents().newLine();
|
||||
}
|
||||
getContents().createFragment(Style.YELLOW_DARK).append(alias).append(": ");
|
||||
getContents().append(description);
|
||||
first = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting.component;
|
||||
|
||||
import com.sk89q.minecraft.util.commands.CommandLocals;
|
||||
import com.sk89q.worldedit.extension.platform.CommandManager;
|
||||
import com.sk89q.worldedit.util.command.CommandCallable;
|
||||
import com.sk89q.worldedit.util.command.CommandMapping;
|
||||
import com.sk89q.worldedit.util.command.Description;
|
||||
import com.sk89q.worldedit.util.command.Dispatcher;
|
||||
import com.sk89q.worldedit.util.command.PrimaryAliasComparator;
|
||||
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A box to describe usage of a command.
|
||||
*/
|
||||
public class CommandUsageBox extends StyledFragment {
|
||||
|
||||
/**
|
||||
* Create a new usage box.
|
||||
*
|
||||
* @param command the command to describe
|
||||
* @param commandString the command that was used, such as "/we" or "/brush sphere"
|
||||
*/
|
||||
public CommandUsageBox(CommandCallable command, String commandString) {
|
||||
this(command, commandString, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new usage box.
|
||||
*
|
||||
* @param command the command to describe
|
||||
* @param commandString the command that was used, such as "/we" or "/brush sphere"
|
||||
* @param locals list of locals to use
|
||||
*/
|
||||
public CommandUsageBox(CommandCallable command, String commandString, @Nullable CommandLocals locals) {
|
||||
checkNotNull(command);
|
||||
checkNotNull(commandString);
|
||||
if (command instanceof Dispatcher) {
|
||||
attachDispatcherUsage((Dispatcher) command, commandString, locals);
|
||||
} else {
|
||||
attachCommandUsage(command.getDescription(), commandString);
|
||||
}
|
||||
}
|
||||
|
||||
private void attachDispatcherUsage(Dispatcher dispatcher, String commandString, @Nullable CommandLocals locals) {
|
||||
CommandListBox box = new CommandListBox("Subcommands");
|
||||
String prefix = !commandString.isEmpty() ? commandString + " " : "";
|
||||
|
||||
List<CommandMapping> list = new ArrayList<CommandMapping>(dispatcher.getCommands());
|
||||
Collections.sort(list, new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN));
|
||||
|
||||
for (CommandMapping mapping : list) {
|
||||
if (locals == null || mapping.getCallable().testPermission(locals)) {
|
||||
box.appendCommand(prefix + mapping.getPrimaryAlias(), mapping.getDescription().getShortDescription());
|
||||
}
|
||||
}
|
||||
|
||||
append(box);
|
||||
}
|
||||
|
||||
private void attachCommandUsage(Description description, String commandString) {
|
||||
MessageBox box = new MessageBox("Help for " + commandString);
|
||||
StyledFragment contents = box.getContents();
|
||||
|
||||
if (description.getUsage() != null) {
|
||||
contents.append(new Label().append("Usage: "));
|
||||
contents.append(description.getUsage());
|
||||
} else {
|
||||
contents.append(new Subtle().append("Usage information is not available."));
|
||||
}
|
||||
|
||||
contents.newLine();
|
||||
|
||||
if (description.getHelp() != null) {
|
||||
contents.append(description.getHelp());
|
||||
} else if (description.getShortDescription() != null) {
|
||||
contents.append(description.getShortDescription());
|
||||
} else {
|
||||
contents.append(new Subtle().append("No further help is available."));
|
||||
}
|
||||
|
||||
append(box);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting.component;
|
||||
|
||||
import com.sk89q.worldedit.util.formatting.Style;
|
||||
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||
|
||||
/**
|
||||
* Represents a fragment representing a label.
|
||||
*/
|
||||
public class Label extends StyledFragment {
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
public Label() {
|
||||
super(Style.YELLOW);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting.component;
|
||||
|
||||
import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
|
||||
import com.sk89q.worldedit.util.formatting.Style;
|
||||
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Makes for a box with a border above and below.
|
||||
*/
|
||||
public class MessageBox extends StyledFragment {
|
||||
|
||||
private final StyledFragment contents = new StyledFragment();
|
||||
|
||||
/**
|
||||
* Create a new box.
|
||||
*/
|
||||
public MessageBox(String title) {
|
||||
checkNotNull(title);
|
||||
|
||||
int leftOver = ColorCodeBuilder.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH - title.length() - 2;
|
||||
int leftSide = (int) Math.floor(leftOver * 1.0/3);
|
||||
int rightSide = (int) Math.floor(leftOver * 2.0/3);
|
||||
if (leftSide > 0) {
|
||||
createFragment(Style.YELLOW).append(createBorder(leftSide));
|
||||
}
|
||||
append(" ");
|
||||
append(title);
|
||||
append(" ");
|
||||
if (rightSide > 0) {
|
||||
createFragment(Style.YELLOW).append(createBorder(rightSide));
|
||||
}
|
||||
newLine();
|
||||
append(contents);
|
||||
}
|
||||
|
||||
private String createBorder(int count) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < count; i++) {
|
||||
builder.append("-");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internal contents.
|
||||
*
|
||||
* @return the contents
|
||||
*/
|
||||
public StyledFragment getContents() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.formatting.component;
|
||||
|
||||
import com.sk89q.worldedit.util.formatting.Style;
|
||||
import com.sk89q.worldedit.util.formatting.StyledFragment;
|
||||
|
||||
/**
|
||||
* Represents a subtle part of the message.
|
||||
*/
|
||||
public class Subtle extends StyledFragment {
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
public Subtle() {
|
||||
super(Style.GRAY);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.function;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Provides a Levenshtein distance between a given string and each string
|
||||
* that this function is applied to.
|
||||
*/
|
||||
public class LevenshteinDistance implements Function<String, Integer> {
|
||||
|
||||
public final static Pattern STANDARD_CHARS = Pattern.compile("[ _\\-]");
|
||||
|
||||
private final String baseString;
|
||||
private final boolean caseSensitive;
|
||||
private final Pattern replacePattern;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param baseString the string to compare to
|
||||
* @param caseSensitive true to make case sensitive comparisons
|
||||
*/
|
||||
public LevenshteinDistance(String baseString, boolean caseSensitive) {
|
||||
this(baseString, caseSensitive, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param baseString the string to compare to
|
||||
* @param caseSensitive true to make case sensitive comparisons
|
||||
* @param replacePattern pattern to match characters to be removed in both the input and test strings (may be null)
|
||||
*/
|
||||
public LevenshteinDistance(String baseString, boolean caseSensitive, @Nullable Pattern replacePattern) {
|
||||
checkNotNull(baseString);
|
||||
this.caseSensitive = caseSensitive;
|
||||
this.replacePattern = replacePattern;
|
||||
baseString = caseSensitive ? baseString : baseString.toLowerCase();
|
||||
baseString = replacePattern != null ? replacePattern.matcher(baseString).replaceAll("") : baseString;
|
||||
this.baseString = baseString;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer apply(String input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (replacePattern != null) {
|
||||
input = replacePattern.matcher(input).replaceAll("");
|
||||
}
|
||||
|
||||
if (caseSensitive) {
|
||||
return distance(baseString, input);
|
||||
} else {
|
||||
return distance(baseString, input.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Find the Levenshtein distance between two Strings.</p>
|
||||
*
|
||||
* <p>This is the number of changes needed to change one String into
|
||||
* another, where each change is a single character modification (deletion,
|
||||
* insertion or substitution).</p>
|
||||
*
|
||||
* <p>The previous implementation of the Levenshtein distance algorithm
|
||||
* was from <a href="http://www.merriampark.com/ld.htm">http://www.merriampark.com/ld.htm</a></p>
|
||||
*
|
||||
* <p>Chas Emerick has written an implementation in Java, which avoids an OutOfMemoryError
|
||||
* which can occur when my Java implementation is used with very large strings.<br>
|
||||
* This implementation of the Levenshtein distance algorithm
|
||||
* is from <a href="http://www.merriampark.com/ldjava.htm">http://www.merriampark.com/ldjava.htm</a></p>
|
||||
*
|
||||
* <pre>
|
||||
* distance(null, *) = IllegalArgumentException
|
||||
* distance(*, null) = IllegalArgumentException
|
||||
* distance("","") = 0
|
||||
* distance("","a") = 1
|
||||
* distance("aaapppp", "") = 7
|
||||
* distance("frog", "fog") = 1
|
||||
* distance("fly", "ant") = 3
|
||||
* distance("elephant", "hippo") = 7
|
||||
* distance("hippo", "elephant") = 7
|
||||
* distance("hippo", "zzzzzzzz") = 8
|
||||
* distance("hello", "hallo") = 1
|
||||
* </pre>
|
||||
*
|
||||
* @param s the first String, must not be null
|
||||
* @param t the second String, must not be null
|
||||
* @return result distance
|
||||
* @throws IllegalArgumentException if either String input {@code null}
|
||||
*/
|
||||
public static int distance(String s, String t) {
|
||||
if (s == null || t == null) {
|
||||
throw new IllegalArgumentException("Strings must not be null");
|
||||
}
|
||||
|
||||
/*
|
||||
* The difference between this impl. and the previous is that, rather
|
||||
* than creating and retaining a matrix of size s.length()+1 by
|
||||
* t.length()+1, we maintain two single-dimensional arrays of length
|
||||
* s.length()+1. The first, d, is the 'current working' distance array
|
||||
* that maintains the newest distance cost counts as we iterate through
|
||||
* the characters of String s. Each time we increment the index of
|
||||
* String t we are comparing, d is copied to p, the second int[]. Doing
|
||||
* so allows us to retain the previous cost counts as required by the
|
||||
* algorithm (taking the minimum of the cost count to the left, up one,
|
||||
* and diagonally up and to the left of the current cost count being
|
||||
* calculated). (Note that the arrays aren't really copied anymore, just
|
||||
* switched...this is clearly much better than cloning an array or doing
|
||||
* a System.arraycopy() each time through the outer loop.)
|
||||
*
|
||||
* Effectively, the difference between the two implementations is this
|
||||
* one does not cause an out of memory condition when calculating the LD
|
||||
* over two very large strings.
|
||||
*/
|
||||
|
||||
int n = s.length(); // length of s
|
||||
int m = t.length(); // length of t
|
||||
|
||||
if (n == 0) {
|
||||
return m;
|
||||
} else if (m == 0) {
|
||||
return n;
|
||||
}
|
||||
|
||||
int[] p = new int[n + 1]; // 'previous' cost array, horizontally
|
||||
int[] d = new int[n + 1]; // cost array, horizontally
|
||||
int[] _d; // placeholder to assist in swapping p and d
|
||||
|
||||
// indexes into strings s and t
|
||||
int i; // iterates through s
|
||||
int j; // iterates through t
|
||||
|
||||
char tj; // jth character of t
|
||||
|
||||
int cost; // cost
|
||||
|
||||
for (i = 0; i <= n; ++i) {
|
||||
p[i] = i;
|
||||
}
|
||||
|
||||
for (j = 1; j <= m; ++j) {
|
||||
tj = t.charAt(j - 1);
|
||||
d[0] = j;
|
||||
|
||||
for (i = 1; i <= n; ++i) {
|
||||
cost = s.charAt(i - 1) == tj ? 0 : 1;
|
||||
// minimum of cell to the left+1, to the top+1, diagonally left
|
||||
// and up +cost
|
||||
d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1]
|
||||
+ cost);
|
||||
}
|
||||
|
||||
// copy current distance counts to 'previous row' distance counts
|
||||
_d = p;
|
||||
p = d;
|
||||
d = _d;
|
||||
}
|
||||
|
||||
// our last action in the above loop was to switch d and p, so p now
|
||||
// actually has the most recent cost counts
|
||||
return p[n];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.gson;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
/**
|
||||
* Utility methods for Google's GSON library.
|
||||
*/
|
||||
public final class GsonUtil {
|
||||
|
||||
private GsonUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a standard {@link GsonBuilder} for WorldEdit.
|
||||
*
|
||||
* @return a builder
|
||||
*/
|
||||
public static GsonBuilder createBuilder() {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(Vector.class, new VectorAdapter());
|
||||
return gsonBuilder;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.gson;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Deserializes {@code Vector}s for GSON.
|
||||
*/
|
||||
public class VectorAdapter implements JsonDeserializer<Vector> {
|
||||
|
||||
@Override
|
||||
public Vector deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonArray jsonArray = json.getAsJsonArray();
|
||||
if (jsonArray.size() != 3) {
|
||||
throw new JsonParseException("Expected array of 3 length for Vector");
|
||||
}
|
||||
|
||||
double x = jsonArray.get(0).getAsDouble();
|
||||
double y = jsonArray.get(1).getAsDouble();
|
||||
double z = jsonArray.get(2).getAsDouble();
|
||||
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
}
|
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.io;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public final class Closer implements Closeable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Closer.class.getCanonicalName());
|
||||
|
||||
/**
|
||||
* The suppressor implementation to use for the current Java version.
|
||||
*/
|
||||
private static final Suppressor SUPPRESSOR = SuppressingSuppressor.isAvailable()
|
||||
? SuppressingSuppressor.INSTANCE
|
||||
: LoggingSuppressor.INSTANCE;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Closer}.
|
||||
*/
|
||||
public static Closer create() {
|
||||
return new Closer(SUPPRESSOR);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
final Suppressor suppressor;
|
||||
|
||||
// only need space for 2 elements in most cases, so try to use the smallest array possible
|
||||
private final Deque<Closeable> stack = new ArrayDeque<Closeable>(4);
|
||||
private Throwable thrown;
|
||||
|
||||
@VisibleForTesting Closer(Suppressor suppressor) {
|
||||
this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given {@code closeable} to be closed when this {@code Closer} is
|
||||
* {@linkplain #close closed}.
|
||||
*
|
||||
* @return the given {@code closeable}
|
||||
*/
|
||||
// close. this word no longer has any meaning to me.
|
||||
public <C extends Closeable> C register(C closeable) {
|
||||
stack.push(closeable);
|
||||
return closeable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given throwable and rethrows it. It will be rethrown as is if it is an
|
||||
* {@code IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown
|
||||
* wrapped in a {@code RuntimeException}. <b>Note:</b> Be sure to declare all of the checked
|
||||
* exception types your try block can throw when calling an overload of this method so as to avoid
|
||||
* losing the original exception type.
|
||||
*
|
||||
* <p>This method always throws, and as such should be called as
|
||||
* {@code throw closer.rethrow(e);} to ensure the compiler knows that it will throw.
|
||||
*
|
||||
* @return this method does not return; it always throws
|
||||
* @throws IOException when the given throwable is an IOException
|
||||
*/
|
||||
public RuntimeException rethrow(Throwable e) throws IOException {
|
||||
thrown = e;
|
||||
Throwables.propagateIfPossible(e, IOException.class);
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given throwable and rethrows it. It will be rethrown as is if it is an
|
||||
* {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of the
|
||||
* given type. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. <b>Note:</b>
|
||||
* Be sure to declare all of the checked exception types your try block can throw when calling an
|
||||
* overload of this method so as to avoid losing the original exception type.
|
||||
*
|
||||
* <p>This method always throws, and as such should be called as
|
||||
* {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw.
|
||||
*
|
||||
* @return this method does not return; it always throws
|
||||
* @throws IOException when the given throwable is an IOException
|
||||
* @throws X when the given throwable is of the declared type X
|
||||
*/
|
||||
public <X extends Exception> RuntimeException rethrow(Throwable e,
|
||||
Class<X> declaredType) throws IOException, X {
|
||||
thrown = e;
|
||||
Throwables.propagateIfPossible(e, IOException.class);
|
||||
Throwables.propagateIfPossible(e, declaredType);
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given throwable and rethrows it. It will be rethrown as is if it is an
|
||||
* {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of either
|
||||
* of the given types. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}.
|
||||
* <b>Note:</b> Be sure to declare all of the checked exception types your try block can throw
|
||||
* when calling an overload of this method so as to avoid losing the original exception type.
|
||||
*
|
||||
* <p>This method always throws, and as such should be called as
|
||||
* {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw.
|
||||
*
|
||||
* @return this method does not return; it always throws
|
||||
* @throws IOException when the given throwable is an IOException
|
||||
* @throws X1 when the given throwable is of the declared type X1
|
||||
* @throws X2 when the given throwable is of the declared type X2
|
||||
*/
|
||||
public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow(
|
||||
Throwable e, Class<X1> declaredType1, Class<X2> declaredType2) throws IOException, X1, X2 {
|
||||
thrown = e;
|
||||
Throwables.propagateIfPossible(e, IOException.class);
|
||||
Throwables.propagateIfPossible(e, declaredType1, declaredType2);
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all {@code Closeable} instances that have been added to this {@code Closer}. If an
|
||||
* exception was thrown in the try block and passed to one of the {@code exceptionThrown} methods,
|
||||
* any exceptions thrown when attempting to close a closeable will be suppressed. Otherwise, the
|
||||
* <i>first</i> exception to be thrown from an attempt to close a closeable will be thrown and any
|
||||
* additional exceptions that are thrown after that will be suppressed.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
Throwable throwable = thrown;
|
||||
|
||||
// close closeables in LIFO order
|
||||
while (!stack.isEmpty()) {
|
||||
Closeable closeable = stack.pop();
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (Throwable e) {
|
||||
if (throwable == null) {
|
||||
throwable = e;
|
||||
} else {
|
||||
suppressor.suppress(closeable, throwable, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thrown == null && throwable != null) {
|
||||
Throwables.propagateIfPossible(throwable, IOException.class);
|
||||
throw new AssertionError(throwable); // not possible
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppression strategy interface.
|
||||
*/
|
||||
@VisibleForTesting interface Suppressor {
|
||||
/**
|
||||
* Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close
|
||||
* the given closeable. {@code thrown} is the exception that is actually being thrown from the
|
||||
* method. Implementations of this method should not throw under any circumstances.
|
||||
*/
|
||||
void suppress(Closeable closeable, Throwable thrown, Throwable suppressed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppresses exceptions by logging them.
|
||||
*/
|
||||
@VisibleForTesting static final class LoggingSuppressor implements Suppressor {
|
||||
|
||||
static final LoggingSuppressor INSTANCE = new LoggingSuppressor();
|
||||
|
||||
@Override
|
||||
public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
|
||||
// log to the same place as Closeables
|
||||
logger.log(Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppresses exceptions by adding them to the exception that will be thrown using JDK7's
|
||||
* addSuppressed(Throwable) mechanism.
|
||||
*/
|
||||
@VisibleForTesting static final class SuppressingSuppressor implements Suppressor {
|
||||
|
||||
static final SuppressingSuppressor INSTANCE = new SuppressingSuppressor();
|
||||
|
||||
static boolean isAvailable() {
|
||||
return addSuppressed != null;
|
||||
}
|
||||
|
||||
static final Method addSuppressed = getAddSuppressed();
|
||||
|
||||
private static Method getAddSuppressed() {
|
||||
try {
|
||||
return Throwable.class.getMethod("addSuppressed", Throwable.class);
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
|
||||
// ensure no exceptions from addSuppressed
|
||||
if (thrown == suppressed) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
addSuppressed.invoke(thrown, suppressed);
|
||||
} catch (Throwable e) {
|
||||
// if, somehow, IllegalAccessException or another exception is thrown, fall back to logging
|
||||
LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ForwardSeekableInputStream extends InputStream {
|
||||
|
||||
protected InputStream parent;
|
||||
protected long position = 0;
|
||||
|
||||
public ForwardSeekableInputStream(InputStream parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int ret = parent.read();
|
||||
++position;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return parent.available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
parent.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
parent.mark(readlimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return parent.markSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
int read = super.read(b, off, len);
|
||||
position += read;
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
int read = parent.read(b);
|
||||
position += read;
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
parent.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
long skipped = parent.skip(n);
|
||||
position += skipped;
|
||||
return skipped;
|
||||
}
|
||||
|
||||
public void seek(long n) throws IOException {
|
||||
long diff = n - position;
|
||||
|
||||
if (diff < 0) {
|
||||
throw new IOException("Can't seek backwards");
|
||||
}
|
||||
|
||||
if (diff == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (skip(diff) < diff) {
|
||||
throw new IOException("Failed to seek " + diff + " bytes");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.io.file;
|
||||
|
||||
public class FileSelectionAbortedException extends FilenameException {
|
||||
|
||||
public FileSelectionAbortedException() {
|
||||
super("");
|
||||
}
|
||||
|
||||
public FileSelectionAbortedException(String msg) {
|
||||
super("", msg);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.io.file;
|
||||
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
|
||||
public class FilenameException extends WorldEditException {
|
||||
|
||||
private String filename;
|
||||
|
||||
public FilenameException(String filename) {
|
||||
super();
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public FilenameException(String filename, String msg) {
|
||||
super(msg);
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.io.file;
|
||||
|
||||
public class FilenameResolutionException extends FilenameException {
|
||||
|
||||
public FilenameResolutionException(String filename) {
|
||||
super(filename);
|
||||
}
|
||||
|
||||
public FilenameResolutionException(String filename, String msg) {
|
||||
super(filename, msg);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.io.file;
|
||||
|
||||
public class InvalidFilenameException extends FilenameException {
|
||||
|
||||
public InvalidFilenameException(String filename) {
|
||||
super(filename);
|
||||
}
|
||||
|
||||
public InvalidFilenameException(String filename, String msg) {
|
||||
super(filename, msg);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.logging;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
* A {@link StreamHandler} delegate that allows for the swap and disable of
|
||||
* another handler. When {@link #setHandler(StreamHandler)} is called with
|
||||
* null, then records passed onto this handler will be dropped. Otherwise,
|
||||
* the delegate handler will receive those records.
|
||||
*/
|
||||
public class DynamicStreamHandler extends StreamHandler {
|
||||
|
||||
private @Nullable StreamHandler handler;
|
||||
private @Nullable Formatter formatter;
|
||||
private @Nullable Filter filter;
|
||||
private @Nullable String encoding;
|
||||
private Level level = Level.ALL;
|
||||
|
||||
/**
|
||||
* Get the delegate handler.
|
||||
*
|
||||
* @return the delegate handler (Which may be null)
|
||||
*/
|
||||
public @Nullable synchronized StreamHandler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the handler.
|
||||
*
|
||||
* @param handler the delegate handler (which can be null)
|
||||
*/
|
||||
public synchronized void setHandler(@Nullable StreamHandler handler) {
|
||||
if (this.handler != null) {
|
||||
this.handler.close();
|
||||
}
|
||||
|
||||
this.handler = handler;
|
||||
|
||||
if (handler != null) {
|
||||
handler.setFormatter(formatter);
|
||||
handler.setFilter(filter);
|
||||
try {
|
||||
handler.setEncoding(encoding);
|
||||
} catch (UnsupportedEncodingException ignore) {
|
||||
}
|
||||
handler.setLevel(level);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void publish(LogRecord record) {
|
||||
if (handler != null) {
|
||||
handler.publish(record);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws SecurityException {
|
||||
if (handler != null) {
|
||||
handler.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(@Nullable String encoding) throws SecurityException, UnsupportedEncodingException {
|
||||
StreamHandler handler = this.handler;
|
||||
this.encoding = encoding;
|
||||
if (handler != null) {
|
||||
handler.setEncoding(encoding);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(LogRecord record) {
|
||||
StreamHandler handler = this.handler;
|
||||
return handler != null && handler.isLoggable(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
StreamHandler handler = this.handler;
|
||||
if (handler != null) {
|
||||
handler.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFormatter(@Nullable Formatter newFormatter) throws SecurityException {
|
||||
StreamHandler handler = this.handler;
|
||||
this.formatter = newFormatter;
|
||||
if (handler != null) {
|
||||
handler.setFormatter(newFormatter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Formatter getFormatter() {
|
||||
StreamHandler handler = this.handler;
|
||||
Formatter formatter = this.formatter;
|
||||
if (handler != null) {
|
||||
return handler.getFormatter();
|
||||
} else if (formatter != null) {
|
||||
return formatter;
|
||||
} else {
|
||||
return new SimpleFormatter();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncoding() {
|
||||
StreamHandler handler = this.handler;
|
||||
String encoding = this.encoding;
|
||||
if (handler != null) {
|
||||
return handler.getEncoding();
|
||||
} else {
|
||||
return encoding;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilter(@Nullable Filter newFilter) throws SecurityException {
|
||||
StreamHandler handler = this.handler;
|
||||
this.filter = newFilter;
|
||||
if (handler != null) {
|
||||
handler.setFilter(newFilter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
StreamHandler handler = this.handler;
|
||||
Filter filter = this.filter;
|
||||
if (handler != null) {
|
||||
return handler.getFilter();
|
||||
} else {
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setLevel(Level newLevel) throws SecurityException {
|
||||
if (handler != null) {
|
||||
handler.setLevel(newLevel);
|
||||
}
|
||||
this.level = newLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Level getLevel() {
|
||||
if (handler != null) {
|
||||
return handler.getLevel();
|
||||
} else {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.logging;
|
||||
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Level;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* A standard logging format for WorldEdit.
|
||||
*/
|
||||
public class LogFormat extends Formatter {
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
Level level = record.getLevel();
|
||||
|
||||
if (level == Level.FINEST) {
|
||||
text.append("[FINEST] ");
|
||||
} else if (level == Level.FINER) {
|
||||
text.append("[FINER] ");
|
||||
} else if (level == Level.FINE) {
|
||||
text.append("[FINE] ");
|
||||
} else if (level == Level.INFO) {
|
||||
text.append("[INFO] ");
|
||||
} else if (level == Level.WARNING) {
|
||||
text.append("[WARNING] ");
|
||||
} else if (level == Level.SEVERE) {
|
||||
text.append("[SEVERE] ");
|
||||
}
|
||||
|
||||
text.append(record.getMessage());
|
||||
text.append("\r\n");
|
||||
|
||||
Throwable t = record.getThrown();
|
||||
if (t != null) {
|
||||
StringWriter writer = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(writer));
|
||||
text.append(writer);
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.logging;
|
||||
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Adds a WorldEdit prefix to WorldEdit's logger messages using a handler.
|
||||
*/
|
||||
public final class WorldEditPrefixHandler extends Handler {
|
||||
|
||||
private WorldEditPrefixHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
String message = record.getMessage();
|
||||
if (!message.startsWith("WorldEdit: ") && !message.startsWith("[WorldEdit] ")) {
|
||||
record.setMessage("[WorldEdit] " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the handler to the following logger name.
|
||||
*
|
||||
* @param name the logger name
|
||||
*/
|
||||
public static void register(String name) {
|
||||
Logger.getLogger(name).addHandler(new WorldEditPrefixHandler());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user