Refactored Namespaced stuff

- Namespaced now takes full ID in constructor
  - Changed the rest of source accordingly
- Moved everything related to Namespaced into .util.namespaced package
- Errors are now reported with IllegalIdException instead of IAE
- Error checking is optimized
- NamespacedUtil exposes more of its requirements
- Added StringUtil.splitAt
- Added ArrayUtil.isSorted
This commit is contained in:
OLEGSHA 2020-11-17 12:46:33 +03:00
parent b51b3a4d80
commit 5d7cfdb3bc
62 changed files with 635 additions and 303 deletions

View File

@ -66,6 +66,18 @@ public class ArrayUtil {
return -1;
}
public static boolean isSorted(byte[] array, boolean ascending) {
for (int i = 0; i < array.length - 1; ++i) {
if (array[i] == array[i + 1]) continue;
if ((array[i] < array[i + 1]) != ascending) {
return false;
}
}
return true;
}
public static int firstIndexOf(short[] array, short element) {
for (int i = 0; i < array.length; ++i) {
if (array[i] == element) {
@ -107,6 +119,18 @@ public class ArrayUtil {
return -1;
}
public static boolean isSorted(short[] array, boolean ascending) {
for (int i = 0; i < array.length - 1; ++i) {
if (array[i] == array[i + 1]) continue;
if ((array[i] < array[i + 1]) != ascending) {
return false;
}
}
return true;
}
public static int firstIndexOf(int[] array, int element) {
for (int i = 0; i < array.length; ++i) {
if (array[i] == element) {
@ -148,6 +172,18 @@ public class ArrayUtil {
return -1;
}
public static boolean isSorted(int[] array, boolean ascending) {
for (int i = 0; i < array.length - 1; ++i) {
if (array[i] == array[i + 1]) continue;
if ((array[i] < array[i + 1]) != ascending) {
return false;
}
}
return true;
}
public static int firstIndexOf(long[] array, long element) {
for (int i = 0; i < array.length; ++i) {
if (array[i] == element) {
@ -189,6 +225,18 @@ public class ArrayUtil {
return -1;
}
public static boolean isSorted(long[] array, boolean ascending) {
for (int i = 0; i < array.length - 1; ++i) {
if (array[i] == array[i + 1]) continue;
if ((array[i] < array[i + 1]) != ascending) {
return false;
}
}
return true;
}
public static int firstIndexOf(float[] array, float element) {
for (int i = 0; i < array.length; ++i) {
if (array[i] == element) {
@ -230,6 +278,18 @@ public class ArrayUtil {
return -1;
}
public static boolean isSorted(float[] array, boolean ascending) {
for (int i = 0; i < array.length - 1; ++i) {
if (array[i] == array[i + 1]) continue;
if ((array[i] < array[i + 1]) != ascending) {
return false;
}
}
return true;
}
public static int firstIndexOf(double[] array, double element) {
for (int i = 0; i < array.length; ++i) {
if (array[i] == element) {
@ -271,6 +331,18 @@ public class ArrayUtil {
return -1;
}
public static boolean isSorted(double[] array, boolean ascending) {
for (int i = 0; i < array.length - 1; ++i) {
if (array[i] == array[i + 1]) continue;
if ((array[i] < array[i + 1]) != ascending) {
return false;
}
}
return true;
}
public static int firstIndexOf(boolean[] array, boolean element) {
for (int i = 0; i < array.length; ++i) {
if (array[i] == element) {
@ -340,6 +412,18 @@ public class ArrayUtil {
return -1;
}
public static boolean isSorted(char[] array, boolean ascending) {
for (int i = 0; i < array.length - 1; ++i) {
if (array[i] == array[i + 1]) continue;
if ((array[i] < array[i + 1]) != ascending) {
return false;
}
}
return true;
}
public static int firstIndexOf(Object[] array, Object element) {
for (int i = 0; i < array.length; ++i) {
if (array[i] == element) {
@ -422,6 +506,20 @@ public class ArrayUtil {
return -1;
}
public static <T extends Comparable<T>> boolean isSorted(T[] array, boolean ascending) {
for (int i = 0; i < array.length - 1; ++i) {
if (array[i] == array[i + 1]) continue;
int order = array[i].compareTo(array[i + 1]);
if ((order < 0) != ascending) {
return false;
}
}
return true;
}
public static long sum(byte[] array, int start, int length) {
long s = 0;
length += start;

View File

@ -29,6 +29,8 @@ import java.util.Iterator;
import java.util.Objects;
import java.util.function.IntFunction;
import ru.windcorp.jputil.ArrayUtil;
public class StringUtil {
private StringUtil() {}
@ -369,6 +371,106 @@ public class StringUtil {
return result;
}
/**
* Splits {@code src} at index {@code at} discarding the character at that index.
* <p>
* Indices {@code 0} and {@code src.length() - 1} produce {@code str} excluding
* the specified character and {@code ""}.
* <p>
* @param src the String to split
* @param at index to split at
* @throws IllegalArgumentException if the index is out of bounds for {@code src}
* @return an array containing the substrings, in order of encounter in {@code src}.
* Its length is always 2.
*/
public static String[] splitAt(String src, int at) {
Objects.requireNonNull(src, "src");
if (at < 0) {
throw new StringIndexOutOfBoundsException(at);
} else if (at >= src.length()) {
throw new StringIndexOutOfBoundsException(at);
}
if (at == 0) {
return new String[] {"", src.substring(1)};
} else if (at == src.length()) {
return new String[] {src.substring(0, src.length() - 1), ""};
}
return new String[] {
src.substring(0, at),
src.substring(at + 1)
};
}
/**
* Splits {@code src} at indices {@code at} discarding characters at those indices.
* <p>
* Indices {@code 0} and {@code src.length() - 1} produce extra zero-length outputs.
* Duplicate indices produce extra zero-length outputs.
* <p>
* Examples:
* <pre>
* splitAt("a.b.c", new int[] {1, 3}) -> {"a", "b", "c"}
* splitAt("a..b", new int[] {1, 2}) -> {"a", "", "b"}
* splitAt(".b.", new int[] {0, 2}) -> {"", "b", ""}
* splitAt("a.b", new int[] {1, 1, 1}) -> {"a", "", "", "b"}
* </pre>
* @param src the String to split
* @param at indices to split at, in any order
* @throws IllegalArgumentException if some index is out of bounds for {@code src}
* @return an array containing the substrings, in order of encounter in {@code src}.
* Its length is always {@code at.length + 1}.
*/
public static String[] splitAt(String src, int... at) {
Objects.requireNonNull(src, "src");
Objects.requireNonNull(at, "at");
if (at.length == 0) return new String[] {src};
if (at.length == 1) return splitAt(src, at[0]);
int[] indices; // Always sorted
if (ArrayUtil.isSorted(at, true)) {
indices = at;
} else {
indices = at.clone();
Arrays.sort(indices);
}
if (indices[0] < 0) {
throw new StringIndexOutOfBoundsException(indices[0]);
} else if (indices[indices.length - 1] >= src.length()) {
throw new StringIndexOutOfBoundsException(indices[indices.length - 1]);
}
String[] result = new String[at.length + 1];
int start = 0;
int resultIndex = 0;
for (int index : indices) {
int end = index;
String substring;
if (end <= start) {
// Duplicate or successive index
substring = "";
} else {
substring = src.substring(start, end);
}
result[resultIndex] = substring;
resultIndex++;
start = end + 1;
}
result[resultIndex] = src.substring(start);
return result;
}
private static IllegalArgumentException illegalArrayLength(int length) {
return new IllegalArgumentException("arrayLength must be non-negative (" + length + ")");
}

View File

@ -1,11 +1,11 @@
package ru.windcorp.progressia.client.comms.controls;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
public abstract class ControlTrigger extends Namespaced {
public ControlTrigger(String namespace, String name) {
super(namespace, name);
public ControlTrigger(String id) {
super(id);
}
}

View File

@ -5,8 +5,8 @@ import ru.windcorp.progressia.common.comms.controls.PacketControl;
public abstract class ControlTriggerInputBased extends ControlTrigger {
public ControlTriggerInputBased(String namespace, String name) {
super(namespace, name);
public ControlTriggerInputBased(String id) {
super(id);
}
public abstract PacketControl onInputEvent(InputEvent event);

View File

@ -6,6 +6,7 @@ import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry;
import ru.windcorp.progressia.common.comms.controls.PacketControl;
import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil;
public class ControlTriggerOnKeyPress extends ControlTriggerInputBased {
@ -13,13 +14,13 @@ public class ControlTriggerOnKeyPress extends ControlTriggerInputBased {
private final PacketControl packet;
public ControlTriggerOnKeyPress(
String namespace, String name,
String id,
Predicate<KeyEvent> predicate
) {
super(namespace, name);
super(id);
this.predicate = predicate;
this.packet = new PacketControl(
getNamespace(), "ControlKeyPress" + getName(),
NamespacedUtil.getId(getNamespace(), "ControlKeyPress" + getName()),
ControlDataRegistry.getInstance().get(getId())
);
}

View File

@ -1,6 +1,6 @@
package ru.windcorp.progressia.client.comms.controls;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class ControlTriggerRegistry extends NamespacedRegistry<ControlTrigger> {

View File

@ -18,13 +18,13 @@
package ru.windcorp.progressia.client.world.block;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.common.util.Namespaced;
public abstract class BlockRender extends Namespaced {
public BlockRender(String namespace, String name) {
super(namespace, name);
public BlockRender(String id) {
super(id);
}
public void render(ShapeRenderHelper renderer) {

View File

@ -22,8 +22,8 @@ import ru.windcorp.progressia.client.graphics.model.Renderable;
public class BlockRenderNone extends BlockRender {
public BlockRenderNone(String namespace, String name) {
super(namespace, name);
public BlockRenderNone(String id) {
super(id);
}
@Override

View File

@ -23,25 +23,22 @@ import ru.windcorp.progressia.common.world.block.BlockFace;
public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
public BlockRenderOpaqueCube(
String namespace, String name,
String id,
Texture topTexture, Texture bottomTexture,
Texture northTexture, Texture southTexture,
Texture eastTexture, Texture westTexture
) {
super(
namespace, name,
id,
topTexture, bottomTexture,
northTexture, southTexture,
eastTexture, westTexture
);
}
public BlockRenderOpaqueCube(
String namespace, String name,
Texture texture
) {
public BlockRenderOpaqueCube(String id, Texture texture) {
this(
namespace, name,
id,
texture, texture,
texture, texture,
texture, texture

View File

@ -22,7 +22,7 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class BlockRenderRegistry extends NamespacedRegistry<BlockRender> {

View File

@ -36,12 +36,12 @@ implements OpaqueCube {
private final Map<BlockFace, Texture> textures = new HashMap<>();
public BlockRenderTexturedCube(
String namespace, String name,
String id,
Texture topTexture, Texture bottomTexture,
Texture northTexture, Texture southTexture,
Texture eastTexture, Texture westTexture
) {
super(namespace, name);
super(id);
textures.put(TOP, topTexture);
textures.put(BOTTOM, bottomTexture);

View File

@ -23,25 +23,22 @@ import ru.windcorp.progressia.common.world.block.BlockFace;
public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
public BlockRenderTransparentCube(
String namespace, String name,
String id,
Texture topTexture, Texture bottomTexture,
Texture northTexture, Texture southTexture,
Texture eastTexture, Texture westTexture
) {
super(
namespace, name,
id,
topTexture, bottomTexture,
northTexture, southTexture,
eastTexture, westTexture
);
}
public BlockRenderTransparentCube(
String namespace, String name,
Texture texture
) {
public BlockRenderTransparentCube(String id, Texture texture) {
this(
namespace, name,
id,
texture, texture,
texture, texture,
texture, texture

View File

@ -19,21 +19,21 @@ package ru.windcorp.progressia.client.world.cro;
import com.google.common.base.Supplier;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
public abstract class ChunkRenderOptimizerSupplier extends Namespaced {
public ChunkRenderOptimizerSupplier(String namespace, String name) {
super(namespace, name);
public ChunkRenderOptimizerSupplier(String id) {
super(id);
}
public abstract ChunkRenderOptimizer createOptimizer();
public static ChunkRenderOptimizerSupplier of(
String namespace, String name,
String id,
Supplier<ChunkRenderOptimizer> supplier
) {
return new ChunkRenderOptimizerSupplier(namespace, name) {
return new ChunkRenderOptimizerSupplier(id) {
@Override
public ChunkRenderOptimizer createOptimizer() {
return supplier.get();

View File

@ -30,7 +30,7 @@ public class ChunkRenderOptimizers {
static {
register(ChunkRenderOptimizerSupplier.of(
"Default", "OpaqueCube",
"Default:OpaqueCube",
ChunkRenderOptimizerCube::new
));
}

View File

@ -1,12 +1,12 @@
package ru.windcorp.progressia.client.world.entity;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.entity.EntityData;
public abstract class EntityRender extends Namespaced {
public EntityRender(String namespace, String name) {
super(namespace, name);
public EntityRender(String id) {
super(id);
}
public abstract EntityRenderable createRenderable(EntityData entity);

View File

@ -6,7 +6,7 @@ import ru.windcorp.progressia.client.graphics.texture.TextureLoader;
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class EntityRenderRegistry extends NamespacedRegistry<EntityRender> {

View File

@ -3,13 +3,13 @@ package ru.windcorp.progressia.client.world.tile;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.block.BlockFace;
public class TileRender extends Namespaced {
public TileRender(String namespace, String name) {
super(namespace, name);
public TileRender(String id) {
super(id);
}
public void render(ShapeRenderHelper renderer, BlockFace face) {

View File

@ -18,10 +18,10 @@ public class TileRenderGrass extends TileRender implements OpaqueTile {
private final Texture sideTexture;
public TileRenderGrass(
String namespace, String name,
String id,
Texture top, Texture side
) {
super(namespace, name);
super(id);
this.topTexture = top;
this.sideTexture = side;
}

View File

@ -22,7 +22,7 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class TileRenderRegistry extends NamespacedRegistry<TileRender> {

View File

@ -16,8 +16,8 @@ public class TileRenderSimple extends TileRender implements OpaqueTile {
private final Texture texture;
public TileRenderSimple(String namespace, String name, Texture texture) {
super(namespace, name);
public TileRenderSimple(String id, Texture texture) {
super(id);
this.texture = texture;
}

View File

@ -1,11 +1,11 @@
package ru.windcorp.progressia.common.comms.controls;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
public class ControlData extends Namespaced {
public ControlData(String namespace, String name) {
super(namespace, name);
public ControlData(String id) {
super(id);
}
}

View File

@ -1,6 +1,6 @@
package ru.windcorp.progressia.common.comms.controls;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class ControlDataRegistry extends NamespacedRegistry<ControlData> {

View File

@ -6,8 +6,8 @@ public class PacketControl extends Packet {
private final ControlData control;
public PacketControl(String namespace, String name, ControlData control) {
super(namespace, name);
public PacketControl(String id, ControlData control) {
super(id);
this.control = control;
}

View File

@ -1,11 +1,11 @@
package ru.windcorp.progressia.common.comms.packets;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
public class Packet extends Namespaced {
public Packet(String namespace, String name) {
super(namespace, name);
public Packet(String id) {
super(id);
}
}

View File

@ -5,7 +5,11 @@ public class PacketSetLocalPlayer extends Packet {
private long localPlayerEntityId;
public PacketSetLocalPlayer(long entityId) {
super("Core", "SetLocalPlayer");
this("Core:SetLocalPlayer", entityId);
}
protected PacketSetLocalPlayer(String id, long entityId) {
super(id);
this.localPlayerEntityId = entityId;
}

View File

@ -4,8 +4,8 @@ import ru.windcorp.progressia.common.world.WorldData;
public abstract class PacketWorldChange extends Packet {
public PacketWorldChange(String namespace, String name) {
super(namespace, name);
public PacketWorldChange(String id) {
super(id);
}
public abstract void apply(WorldData world);

View File

@ -43,10 +43,8 @@ extends AbstractStatefulObjectLayout {
}
@Override
public StateFieldBuilder getBuilder(String namespace, String name) {
return new InspectingStateFieldBuilder(
namespace, name
);
public StateFieldBuilder getBuilder(String id) {
return new InspectingStateFieldBuilder(id);
}
private class InspectingStateFieldBuilder implements StateFieldBuilder {
@ -56,7 +54,7 @@ extends AbstractStatefulObjectLayout {
@Override
public IntStateField build() {
return registerField(new IntStateField(
namespace, name,
id,
isLocal,
fieldIndexCounters.getIntsThenIncrement()
));
@ -64,16 +62,12 @@ extends AbstractStatefulObjectLayout {
}
private final String namespace;
private final String name;
private final String id;
private boolean isLocal = true;
public InspectingStateFieldBuilder(
String namespace, String name
) {
this.namespace = namespace;
this.name = name;
public InspectingStateFieldBuilder(String id) {
this.id = id;
}
@Override

View File

@ -7,11 +7,11 @@ import java.io.IOException;
public class IntStateField extends StateField {
public IntStateField(
String namespace, String name,
String id,
boolean isLocal,
int index
) {
super(namespace, name, isLocal, index);
super(id, isLocal, index);
}
public int get(StatefulObject object) {

View File

@ -35,7 +35,7 @@ extends AbstractStatefulObjectLayout {
}
@Override
public StateFieldBuilder getBuilder(String namespace, String name) {
public StateFieldBuilder getBuilder(String id) {
return new RetrieverStateFieldBuilder();
}

View File

@ -4,7 +4,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
public abstract class StateField extends Namespaced {
@ -12,11 +12,11 @@ public abstract class StateField extends Namespaced {
private final int index;
public StateField(
String namespace, String name,
String id,
boolean isLocal,
int index
) {
super(namespace, name);
super(id);
this.isLocal = isLocal;
this.index = index;
}

View File

@ -5,7 +5,7 @@ import java.io.DataOutput;
import java.io.IOException;
import java.util.Objects;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
/**
* An abstract class describing objects that have trackable state,
@ -44,10 +44,9 @@ public abstract class StatefulObject extends Namespaced {
public StatefulObject(
StatefulObjectRegistry<?> type,
String namespace,
String name
String id
) {
super(namespace, name);
super(id);
this.layout = type.getLayout(getId());
this.storage = getLayout().createStorage();
}
@ -96,8 +95,8 @@ public abstract class StatefulObject extends Namespaced {
*
* @return a configured builder
*/
protected StateFieldBuilder field(String namespace, String name) {
StateFieldBuilder builder = getLayout().getBuilder(namespace, name);
protected StateFieldBuilder field(String id) {
StateFieldBuilder builder = getLayout().getBuilder(id);
builder.setOrdinal(fieldOrdinal);
fieldOrdinal++;

View File

@ -43,6 +43,6 @@ public abstract class StatefulObjectLayout {
public abstract int computeHashCode(StatefulObject object);
public abstract boolean areEqual(StatefulObject a, StatefulObject b);
public abstract StateFieldBuilder getBuilder(String namespace, String name);
public abstract StateFieldBuilder getBuilder(String id);
}

View File

@ -5,8 +5,8 @@ import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
/**
* Registry-like object for identification of various {@link StatefulObject}
@ -30,8 +30,8 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
private final AtomicBoolean isRegistered = new AtomicBoolean(false);
public Type(String namespace, String name, Factory<T> factory) {
super(namespace, name);
public Type(String id, Factory<T> factory) {
super(id);
this.factory = factory;
}
@ -91,8 +91,8 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
return registry.get(id).build();
}
public void register(String namespace, String name, Factory<T> factory) {
registry.register(new Type<>(namespace, name, factory));
public void register(String id, Factory<T> factory) {
registry.register(new Type<>(id, factory));
}
}

View File

@ -1,46 +0,0 @@
package ru.windcorp.progressia.common.util;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public class NamespacedUtil {
public static final char SEPARATOR = ':';
public static final int MAX_PART_LENGTH = 127;
public static final int MAX_TOTAL_LENGTH = MAX_PART_LENGTH * 2 + 1;
private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$";
private static final Predicate<String> PART_CHECKER =
Pattern.compile(PART_REGEX).asPredicate();
public static String getId(String namespace, String name) {
checkPart(namespace, "Namespace");
checkPart(name, "Name");
return namespace + SEPARATOR + name;
}
private static void checkPart(String data, String name) {
Objects.requireNonNull(data, name);
if (data.length() > MAX_PART_LENGTH) {
throw new IllegalArgumentException(
name + " \"" + data + "\" is too long. "
+ "Expected at most " + MAX_PART_LENGTH
+ " characters"
);
}
if (!PART_CHECKER.test(name)) {
throw new IllegalArgumentException(
name + " \"" + data + "\" is invalid. "
+ "Allowed is: " + PART_REGEX
);
}
}
private NamespacedUtil() {}
}

View File

@ -0,0 +1,27 @@
package ru.windcorp.progressia.common.util.namespaces;
public class IllegalIdException extends RuntimeException {
private static final long serialVersionUID = -1572240191058305981L;
public IllegalIdException() {
super();
}
protected IllegalIdException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public IllegalIdException(String message, Throwable cause) {
super(message, cause);
}
public IllegalIdException(String message) {
super(message);
}
public IllegalIdException(Throwable cause) {
super(cause);
}
}

View File

@ -15,50 +15,27 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.progressia.common.util;
package ru.windcorp.progressia.common.util.namespaces;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public abstract class Namespaced {
public abstract class Namespaced extends Named {
private static final char SEPARATOR = ':';
private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$";
private static final Predicate<String> PART_CHECKER =
Pattern.compile(PART_REGEX).asPredicate();
private final String namespace;
private final String id;
public Namespaced(String namespace, String name) {
super(name);
this.namespace = Objects.requireNonNull(namespace, "namespace");
this.id = namespace + SEPARATOR + name;
if (!PART_CHECKER.test(name)) {
throw new IllegalArgumentException(
"Name \"" + name + "\" is invalid. "
+ "Allowed is: " + PART_REGEX
);
public Namespaced(String id) {
NamespacedUtil.checkId(id);
this.id = id;
}
if (!PART_CHECKER.test(namespace)) {
throw new IllegalArgumentException(
"Namespace \"" + namespace + "\" is invalid. "
+ "Allowed is: " + PART_REGEX
);
}
}
public String getId() {
public final String getId() {
return id;
}
public String getNamespace() {
return namespace;
return NamespacedUtil.getNamespace(getId());
}
public String getName() {
return NamespacedUtil.getName(getId());
}
@Override
@ -75,15 +52,10 @@ public abstract class Namespaced extends Named {
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
Namespaced other = (Namespaced) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
if (!id.equals(other.id))
return false;
return true;
}

View File

@ -1,4 +1,4 @@
package ru.windcorp.progressia.common.util;
package ru.windcorp.progressia.common.util.namespaces;
import java.util.Collection;
import java.util.Collections;

View File

@ -0,0 +1,106 @@
package ru.windcorp.progressia.common.util.namespaces;
import java.util.Objects;
import ru.windcorp.jputil.chars.StringUtil;
public class NamespacedUtil {
public static final char SEPARATOR = ':';
public static final int MAX_ID_LENGTH = 255;
private static final int MAX_PART_LENGTH = (MAX_ID_LENGTH - 1) / 2;
public static final int MAX_NAMESPACE_LENGTH = MAX_PART_LENGTH;
public static final int MAX_NAME_LENGTH = MAX_PART_LENGTH;
private static final int MIN_PART_LENGTH = 3;
public static final int MIN_NAMESPACE_LENGTH = MIN_PART_LENGTH;
public static final int MIN_NAME_LENGTH = MIN_PART_LENGTH;
/*
* This is the definition of the accepted pattern, but the value of
* these constants is not actually consulted in the check* methods.
*/
private static final String PART_CORE_REGEX = "[A-Z][a-zA-Z0-9]{2," + (MAX_PART_LENGTH - 1) + "}";
private static final String PART_REGEX = "^" + PART_CORE_REGEX + "$";
public static final String NAMESPACE_REGEX = PART_REGEX;
public static final String NAME_REGEX = PART_REGEX;
public static final String ID_REGEX = "^" + PART_CORE_REGEX + ":" + PART_CORE_REGEX + "$";
public static String getName(String id) {
checkId(id);
return id.substring(id.indexOf(':') + 1);
}
public static String getNamespace(String id) {
checkId(id);
return id.substring(0, id.indexOf(':'));
}
public static String getId(String namespace, String name) {
checkPart(namespace, 0, namespace.length(), "Namespace");
checkPart(name, 0, name.length(), "Name");
return namespace + SEPARATOR + name;
}
public static void checkId(String id) {
Objects.requireNonNull(id, "id");
int firstSeparator = id.indexOf(SEPARATOR);
boolean areSeparatorsInvalid = (firstSeparator < 0) || (id.indexOf(SEPARATOR, firstSeparator + 1) >= 0);
if (areSeparatorsInvalid) {
int separators = StringUtil.count(id, SEPARATOR);
throw new IllegalIdException(
"ID \"" + id + "\" is invalid. "
+ (separators == 0 ? "No " : "Too many (" + separators + ") ")
+ "separators '" + SEPARATOR + "' found, exactly one required"
);
}
checkPart(id, 0, firstSeparator, "namespace");
checkPart(id, firstSeparator + 1, id.length() - firstSeparator - 1, "name");
}
private static void checkPart(String data, int offset, int length, String nameForErrors) {
Objects.requireNonNull(data, nameForErrors);
if (length > MAX_PART_LENGTH) {
throw new IllegalIdException(
nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is too long. "
+ "Expected at most " + MAX_PART_LENGTH
+ " characters"
);
} else if (length < MIN_PART_LENGTH) {
throw new IllegalIdException(
nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is too short. "
+ "Expected at lest " + MIN_PART_LENGTH
+ " characters"
);
}
// Don't actually use *_REGEX for speed
for (int i = 0; i < length; ++i) {
char c = data.charAt(i + offset);
if (!(
( c >= 'A' && c <= 'Z') ||
(i != 0 && c >= 'a' && c <= 'z') ||
(i != 0 && c >= '0' && c <= '9')
)) {
throw new IllegalIdException(
nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is invalid. "
+ "Allowed is: " + PART_REGEX
);
}
}
}
private NamespacedUtil() {}
}

View File

@ -19,12 +19,12 @@ package ru.windcorp.progressia.common.world.block;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
public class BlockData extends Namespaced {
public BlockData(String namespace, String name) {
super(namespace, name);
public BlockData(String id) {
super(id);
}
public CollisionModel getCollisionModel() {

View File

@ -17,7 +17,7 @@
*******************************************************************************/
package ru.windcorp.progressia.common.world.block;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class BlockDataRegistry extends NamespacedRegistry<BlockData> {

View File

@ -20,8 +20,8 @@ public class EntityData extends StatefulObject implements Collideable {
private double age = 0;
public EntityData(String namespace, String name) {
super(EntityDataRegistry.getInstance(), namespace, name);
public EntityData(String id) {
super(EntityDataRegistry.getInstance(), id);
}
public Vec3 getPosition() {

View File

@ -10,8 +10,8 @@ public class EntityDataRegistry extends StatefulObjectRegistry<EntityData> {
return INSTANCE;
}
public void register(String namespace, String name) {
super.register(namespace, name, () -> new EntityData(namespace, name));
public void register(String id) {
super.register(id, () -> new EntityData(id));
}
}

View File

@ -15,7 +15,11 @@ public class PacketEntityChange extends PacketWorldChange {
private final DataBuffer buffer = new DataBuffer();
public PacketEntityChange() {
super("Core", "EntityChange");
super("Core:EntityChange");
}
protected PacketEntityChange(String id) {
super(id);
}
public long getEntityId() {

View File

@ -17,12 +17,12 @@
*******************************************************************************/
package ru.windcorp.progressia.common.world.tile;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
public class TileData extends Namespaced {
public TileData(String namespace, String name) {
super(namespace, name);
public TileData(String id) {
super(id);
}
}

View File

@ -17,7 +17,7 @@
*******************************************************************************/
package ru.windcorp.progressia.common.world.tile;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class TileDataRegistry extends NamespacedRegistry<TileData> {

View File

@ -1,14 +1,14 @@
package ru.windcorp.progressia.server.comms.controls;
import ru.windcorp.progressia.common.comms.controls.PacketControl;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.comms.Client;
public abstract class ControlLogic extends Namespaced {
public ControlLogic(String namespace, String name) {
super(namespace, name);
public ControlLogic(String id) {
super(id);
}
public abstract void apply(

View File

@ -1,6 +1,6 @@
package ru.windcorp.progressia.server.comms.controls;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class ControlLogicRegistry extends NamespacedRegistry<ControlLogic> {

View File

@ -35,7 +35,11 @@ public class ImplementedChangeTracker implements Changer {
private BlockData block;
public SetBlock() {
super("Core", "SetBlock");
this("Core:SetBlock");
}
protected SetBlock(String id) {
super(id);
}
public void initialize(Vec3i position, BlockData block) {
@ -76,7 +80,11 @@ public class ImplementedChangeTracker implements Changer {
private boolean shouldAdd;
public AddOrRemoveTile() {
super("Core", "AddOrRemoveTile");
this("Core:AddOrRemoveTile");
}
protected AddOrRemoveTile(String id) {
super(id);
}
public void initialize(

View File

@ -1,11 +1,11 @@
package ru.windcorp.progressia.server.world.block;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
public class BlockLogic extends Namespaced {
public BlockLogic(String namespace, String name) {
super(namespace, name);
public BlockLogic(String id) {
super(id);
}
}

View File

@ -1,6 +1,6 @@
package ru.windcorp.progressia.server.world.block;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class BlockLogicRegistry extends NamespacedRegistry<BlockLogic> {

View File

@ -1,14 +1,14 @@
package ru.windcorp.progressia.server.world.entity;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.server.world.Changer;
import ru.windcorp.progressia.server.world.TickContext;
public class EntityLogic extends Namespaced {
public EntityLogic(String namespace, String name) {
super(namespace, name);
public EntityLogic(String id) {
super(id);
}
public void tick(EntityData entity, TickContext context, Changer changer) {

View File

@ -1,6 +1,6 @@
package ru.windcorp.progressia.server.world.entity;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class EntityLogicRegistry extends NamespacedRegistry<EntityLogic> {

View File

@ -1,12 +1,12 @@
package ru.windcorp.progressia.server.world.tile;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.block.BlockFace;
public class TileLogic extends Namespaced {
public TileLogic(String namespace, String name) {
super(namespace, name);
public TileLogic(String id) {
super(id);
}
public boolean canOccupyFace(TileTickContext context) {

View File

@ -1,6 +1,6 @@
package ru.windcorp.progressia.server.world.tile;
import ru.windcorp.progressia.common.util.NamespacedRegistry;
import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry;
public class TileLogicRegistry extends NamespacedRegistry<TileLogic> {

View File

@ -42,65 +42,65 @@ public class TestContent {
}
private static void registerBlocks() {
register(new BlockData("Test", "Air") {
register(new BlockData("Test:Air") {
@Override
public CollisionModel getCollisionModel() {
return null;
}
});
register(new BlockRenderNone("Test", "Air"));
register(new BlockLogic("Test", "Air"));
register(new BlockRenderNone("Test:Air"));
register(new BlockLogic("Test:Air"));
register(new BlockData("Test", "Dirt"));
register(new BlockRenderOpaqueCube("Test", "Dirt", getBlockTexture("dirt")));
register(new BlockLogic("Test", "Dirt"));
register(new BlockData("Test:Dirt"));
register(new BlockRenderOpaqueCube("Test:Dirt", getBlockTexture("dirt")));
register(new BlockLogic("Test:Dirt"));
register(new BlockData("Test", "Stone"));
register(new BlockRenderOpaqueCube("Test", "Stone", getBlockTexture("stone")));
register(new BlockLogic("Test", "Stone"));
register(new BlockData("Test:Stone"));
register(new BlockRenderOpaqueCube("Test:Stone", getBlockTexture("stone")));
register(new BlockLogic("Test:Stone"));
register(new BlockData("Test", "Compass"));
register(new BlockRenderOpaqueCube("Test", "Compass", getBlockTexture("compass")));
register(new BlockLogic("Test", "Compass"));
register(new BlockData("Test:Compass"));
register(new BlockRenderOpaqueCube("Test:Compass", getBlockTexture("compass")));
register(new BlockLogic("Test:Compass"));
register(new BlockData("Test", "Glass"));
register(new BlockRenderTransparentCube("Test", "Glass", getBlockTexture("glass_clear")));
register(new BlockLogic("Test", "Glass"));
register(new BlockData("Test:Glass"));
register(new BlockRenderTransparentCube("Test:Glass", getBlockTexture("glass_clear")));
register(new BlockLogic("Test:Glass"));
}
private static void registerTiles() {
register(new TileData("Test", "Grass"));
register(new TileRenderGrass("Test", "Grass", getTileTexture("grass_top"), getTileTexture("grass_side")));
register(new TileLogic("Test", "Grass"));
register(new TileData("Test:Grass"));
register(new TileRenderGrass("Test:Grass", getTileTexture("grass_top"), getTileTexture("grass_side")));
register(new TileLogic("Test:Grass"));
register(new TileData("Test", "Stones"));
register(new TileRenderSimple("Test", "Stones", getTileTexture("stones")));
register(new TileLogic("Test", "Stones"));
register(new TileData("Test:Stones"));
register(new TileRenderSimple("Test:Stones", getTileTexture("stones")));
register(new TileLogic("Test:Stones"));
register(new TileData("Test", "YellowFlowers"));
register(new TileRenderSimple("Test", "YellowFlowers", getTileTexture("yellow_flowers")));
register(new TileLogic("Test", "YellowFlowers"));
register(new TileData("Test:YellowFlowers"));
register(new TileRenderSimple("Test:YellowFlowers", getTileTexture("yellow_flowers")));
register(new TileLogic("Test:YellowFlowers"));
register(new TileData("Test", "Sand"));
register(new TileRenderSimple("Test", "Sand", getTileTexture("sand")));
register(new TileLogic("Test", "Sand"));
register(new TileData("Test:Sand"));
register(new TileRenderSimple("Test:Sand", getTileTexture("sand")));
register(new TileLogic("Test:Sand"));
}
private static void registerEntities() {
float scale = 1.8f / 8;
registerEntityData("Test", "Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f)));
register(new TestEntityRenderHuman());
register(new EntityLogic("Test", "Player"));
registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f)));
register(new TestEntityRenderHuman("Test:Player"));
register(new EntityLogic("Test:Player"));
register("Test", "Statie", TestEntityDataStatie::new);
register(new TestEntityRenderStatie());
register(new TestEntityLogicStatie());
register("Test:Statie", TestEntityDataStatie::new);
register(new TestEntityRenderStatie("Test:Statie"));
register(new TestEntityLogicStatie("Test:Statie"));
}
private static void regsiterControls() {
ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000"));
ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches));
ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "Switch000") {
ControlDataRegistry.getInstance().register(new ControlData("Test:Switch000"));
ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test:Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches));
ControlLogicRegistry.getInstance().register(new ControlLogic("Test:Switch000") {
@Override
public void apply(Server server, PacketControl packet, Client client) {
Vec3i z000 = new Vec3i(0, 0, 0);
@ -117,26 +117,6 @@ public class TestContent {
server.getAdHocChanger().setBlock(z000, block);
}
});
// ControlDataRegistry.getInstance().register(new ControlData("Test", "BreakBlock"));
// ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "BreakBlock", new KeyMatcher(GLFW.GLFW_KEY_ENTER, new int[0], 0)::matches));
// ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "BreakBlock") {
// @Override
// public void apply(Server server, PacketControl packet, Client client) {
// Vec3i z000 = new Vec3i(0, 0, 0);
//
// ChunkData data = server.getWorld().getChunk(z000).getData();
//
// BlockData block;
// if (data.getBlock(z000).getId().equals("Test:Stone")) {
// block = BlockDataRegistry.getInstance().get("Test:Glass");
// } else {
// block = BlockDataRegistry.getInstance().get("Test:Stone");
// }
//
// server.getAdHocChanger().setBlock(z000, block);
// }
// });
}
private static void register(BlockData x) {
@ -148,20 +128,20 @@ public class TestContent {
}
private static void register(
String namespace, String name,
String id,
Factory<EntityData> factory
) {
EntityDataRegistry.getInstance().register(namespace, name, factory);
EntityDataRegistry.getInstance().register(id, factory);
}
private static void registerEntityData(
String namespace, String name,
String id,
Consumer<EntityData> transform
) {
EntityDataRegistry.getInstance().register(namespace, name, new Factory<EntityData>() {
EntityDataRegistry.getInstance().register(id, new Factory<EntityData>() {
@Override
public EntityData build() {
EntityData entity = new EntityData(namespace, name);
EntityData entity = new EntityData(id);
transform.accept(entity);
return entity;
}

View File

@ -7,10 +7,14 @@ import ru.windcorp.progressia.common.world.entity.EntityData;
public class TestEntityDataStatie extends EntityData {
private final IntStateField size =
field("Test", "Size").setShared().ofInt().build();
field("Test:Size").setShared().ofInt().build();
public TestEntityDataStatie() {
super("Test", "Statie");
this("Test:Statie");
}
protected TestEntityDataStatie(String id) {
super(id);
setCollisionModel(new AABB(0, 0, 0, 1, 1, 1));
setSizeNow(16);
}

View File

@ -7,8 +7,8 @@ import ru.windcorp.progressia.server.world.entity.EntityLogic;
public class TestEntityLogicStatie extends EntityLogic {
public TestEntityLogicStatie() {
super("Test", "Statie");
public TestEntityLogicStatie(String id) {
super(id);
}
@Override

View File

@ -9,6 +9,7 @@ import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder;
import ru.windcorp.progressia.client.graphics.model.StaticModel;
import ru.windcorp.progressia.client.graphics.texture.ComplexTexture;
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.entity.HumanoidModel;
import ru.windcorp.progressia.client.world.entity.EntityRender;
@ -30,11 +31,15 @@ public class TestEntityRenderHuman extends EntityRender {
private final Renderable leftLeg;
private final Renderable rightLeg;
public TestEntityRenderHuman() {
super("Test", "Player");
private final TexturePrimitive skin;
public TestEntityRenderHuman(String id) {
super(id);
this.skin = fetchSkin();
ComplexTexture texture = new ComplexTexture(
EntityRenderRegistry.getEntityTexture("pyotr"),
this.skin,
16, 16
);
@ -47,6 +52,14 @@ public class TestEntityRenderHuman extends EntityRender {
this.rightLeg = createLimb(texture, 0, 8, 0, 4, false, false);
}
protected TexturePrimitive fetchSkin() {
return EntityRenderRegistry.getEntityTexture("pyotr");
}
public TexturePrimitive getSkin() {
return skin;
}
private Renderable createBody(ComplexTexture texture) {
return createLayeredCuboid(
texture,

View File

@ -32,8 +32,8 @@ public class TestEntityRenderJavapony extends EntityRender {
private final Renderable rightForeLeg;
private final Renderable rightHindLeg;
public TestEntityRenderJavapony() {
super("Test", "Javapony");
public TestEntityRenderJavapony(String id) {
super(id);
ComplexTexture texture = new ComplexTexture(
EntityRenderRegistry.getEntityTexture("javapony"),

View File

@ -19,8 +19,8 @@ public class TestEntityRenderStatie extends EntityRender {
.setColorMultiplier(1, 1, 0)
.create();
public TestEntityRenderStatie() {
super("Test", "Statie");
public TestEntityRenderStatie(String id) {
super(id);
}
@Override

View File

@ -0,0 +1,70 @@
package ru.windcorp.jputil.chars.stringUtil;
import static org.junit.Assert.assertArrayEquals;
import java.util.Random;
import org.junit.Test;
import ru.windcorp.jputil.chars.StringUtil;
public class SplitAtTest {
@Test
public void testExamplesFromDocs() {
test("a.b.c", new int[] {1, 3}, new String[] {"a", "b", "c"});
test("a..b", new int[] {1, 2}, new String[] {"a", "", "b"});
test(".b.", new int[] {0, 2}, new String[] {"", "b", ""});
test("a.b", new int[] {1, 1, 1}, new String[] {"a", "", "", "b"});
}
@Test
public void testIndexPermutations() {
Random random = new Random(0);
int stringLength = 1000;
char[] chars = new char[stringLength];
for (int i = 0; i < stringLength; ++i) {
chars[i] = (char) ('a' + random.nextInt('z' - 'a'));
}
String src = new String(chars);
int[] indices = new int[100];
for (int i = 0; i < indices.length; ++i) {
indices[i] = random.nextInt(stringLength);
}
String[] expected = StringUtil.splitAt(src, indices);
for (int i = 0; i < 10000; ++i) {
shuffleArray(indices, random);
int[] copy = indices.clone();
test(src, indices, expected);
assertArrayEquals(indices, copy); // Make sure indices array hasn't changed
}
}
// Shamelessly copied from
// https://stackoverflow.com/a/1520212/4463352
// Thanks, https://stackoverflow.com/users/15459/philho!
// Implementing FisherYates shuffle
private static void shuffleArray(int[] ar, Random random) {
for (int i = ar.length - 1; i > 0; i--) {
int index = random.nextInt(i + 1);
// Simple swap
int a = ar[index];
ar[index] = ar[i];
ar[i] = a;
}
}
private void test(String string, int[] at, String[] expecteds) {
assertArrayEquals(expecteds, StringUtil.splitAt(string, at));
}
}

View File

@ -29,38 +29,40 @@ import java.util.Random;
import org.junit.Test;
import junit.framework.AssertionFailedError;
import ru.windcorp.progressia.common.util.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.IllegalIdException;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil;
public class NamespacedTest {
class TestNamespaced extends Namespaced {
public TestNamespaced(String namespace, String name) {
super(namespace, name);
public TestNamespaced(String id) {
super(id);
}
}
void shouldReject(String a, String b) {
try {
new TestNamespaced(a, b);
} catch (IllegalArgumentException | NullPointerException e) {
new TestNamespaced(NamespacedUtil.getId(a, b));
} catch (IllegalIdException | NullPointerException e) {
try {
new TestNamespaced(b, a);
} catch (IllegalArgumentException | NullPointerException e1) {
new TestNamespaced(NamespacedUtil.getId(b, a));
} catch (IllegalIdException | NullPointerException e1) {
return;
}
}
throw new AssertionFailedError("Expected NPE or IAE for: \"" + a + "\":\"" + b + "\"");
throw new AssertionFailedError("Expected NPE or IllegalIdException for: \"" + a + "\":\"" + b + "\"");
}
@Test
public void shouldAllow() {
new TestNamespaced("Something", "Usual");
new TestNamespaced("Vry", "Sml");
new TestNamespaced("ALL", "CAPS");
new TestNamespaced("WithDigits12345", "MoreDigits67890");
new TestNamespaced("Something:Usual");
new TestNamespaced("Vry:Sml");
new TestNamespaced("ALL:CAPS");
new TestNamespaced("WithDigits12345:MoreDigits67890");
}
@Test
@ -80,17 +82,17 @@ public class NamespacedTest {
shouldReject("XS", "Normal");
shouldReject("", "Normal");
shouldReject("Contains:separators", "Normal");
shouldReject("СодержитРусский", "Normal");
shouldReject("СодержитНеАльфанум", "Normal");
}
@Test
public void shouldRejectGarbage() {
Random random = new Random(0);
byte[] bytes = new byte[1024];
byte[] bytes = new byte[NamespacedUtil.MAX_NAME_LENGTH];
for (int attempt = 0; attempt < 10000; ++attempt) {
random.nextBytes(bytes);
bytes[0] = 'a'; // Make sure it is invalid
bytes[bytes.length - 1] = '!'; // Make sure it is invalid
shouldReject(new String(bytes), "ContainsUtterGarbage");
}
}
@ -108,8 +110,8 @@ public class NamespacedTest {
String namespace = getRandomValidString(random);
String name = getRandomValidString(random);
TestNamespaced a = new TestNamespaced(namespace, name);
TestNamespaced b = new TestNamespaced(namespace, name);
TestNamespaced a = new TestNamespaced(NamespacedUtil.getId(namespace, name));
TestNamespaced b = new TestNamespaced(NamespacedUtil.getId(namespace, name));
contains.add(a);
hashSet.add(b);
@ -119,7 +121,7 @@ public class NamespacedTest {
String namespace = getRandomValidString(random);
String name = getRandomValidString(random);
TestNamespaced c = new TestNamespaced(namespace, name);
TestNamespaced c = new TestNamespaced(NamespacedUtil.getId(namespace, name));
doesNotContain.add(c);
}
@ -128,7 +130,7 @@ public class NamespacedTest {
Iterator<TestNamespaced> it = doesNotContain.iterator();
while (it.hasNext()) {
TestNamespaced next = it.next();
if (next.getName().equals(x.getName()) && next.getNamespace().equals(x.getNamespace())) {
if (next.getId().equals(x.getId())) {
it.remove();
}
}
@ -144,7 +146,7 @@ public class NamespacedTest {
}
String getRandomValidString(Random random) {
char[] chars = new char[random.nextInt(100) + 3];
char[] chars = new char[random.nextInt(NamespacedUtil.MAX_NAME_LENGTH - 3) + 3];
for (int i = 0; i < chars.length; ++i) {
switch (random.nextInt(3)) {