diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExposedMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExposedMaskParser.java index dfd0e9ce1..34ea94054 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExposedMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExposedMaskParser.java @@ -46,6 +46,8 @@ public List getMatchedAliases() { @Override public Mask parseFromSimpleInput(String input, ParserContext context) throws InputParseException { - return new OffsetsMask(Masks.negate(new ExistingBlockMask(context.requireExtent())), true); + return OffsetsMask.builder(Masks.negate(new ExistingBlockMask(context.requireExtent()))) + .excludeSelf(true) + .build(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java index 34112f902..3deb0d70d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java @@ -26,7 +26,7 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.MaskIntersection; import com.sk89q.worldedit.function.mask.Masks; -import com.sk89q.worldedit.function.mask.OffsetMask; +import com.sk89q.worldedit.function.mask.OffsetsMask; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.BlockVector3; @@ -63,7 +63,7 @@ public Mask parseFromInput(String input, ParserContext context) throws InputPars } else { submask = new ExistingBlockMask(context.requireExtent()); } - OffsetMask offsetMask = new OffsetMask(submask, BlockVector3.at(0, firstChar == '>' ? -1 : 1, 0)); + Mask offsetMask = OffsetsMask.single(submask, BlockVector3.at(0, firstChar == '>' ? -1 : 1, 0)); return new MaskIntersection(offsetMask, Masks.negate(submask)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetsMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetsMask.java index cc7c39fb5..77678fb7c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetsMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetsMask.java @@ -20,10 +20,10 @@ package com.sk89q.worldedit.function.mask; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Direction; -import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -42,78 +42,115 @@ public class OffsetsMask extends AbstractMask { .collect(Collectors.toList()) ); + /** + * Create an offsets mask for a single offset. + * + * @param mask the mask to use + * @param offset the offset + * @return the new offsets mask + */ + public static OffsetsMask single(Mask mask, BlockVector3 offset) { + return builder(mask).maxMatches(1).offsets(ImmutableList.of(offset)).build(); + } + + /** + * Create a new builder, using the given mask. + * @param mask the mask to use + * @return the builder + */ + public static Builder builder(Mask mask) { + return new Builder().mask(mask); + } + + /** + * A builder for an {@link OffsetsMask}. + */ + public static final class Builder { + private Mask mask; + private boolean excludeSelf; + private int minMatches = 1; + private int maxMatches = Integer.MAX_VALUE; + private ImmutableList offsets = OFFSET_LIST; + + private Builder() { + } + + /** + * Set the mask to test. + * @param mask the mask to test + * @return this builder, for chaining + */ + public Builder mask(Mask mask) { + this.mask = mask; + return this; + } + + /** + * Set whether the mask should fail if the original position matches. Defaults to + * {@code false}. + * + * @param excludeSelf {@code true} to exclude the original position if it matches + * @return this builder, for chaining + */ + public Builder excludeSelf(boolean excludeSelf) { + this.excludeSelf = excludeSelf; + return this; + } + + /** + * Set the minimum amount of matches required. Defaults to {@code 1}. Must be smaller than + * or equal to the {@linkplain #maxMatches(int) max matches} and the {@link #offsets} size, + * and greater than or equal to {@code 0}. + * + * @param minMatches the minimum amount of matches required + * @return this builder, for chaining + */ + public Builder minMatches(int minMatches) { + this.minMatches = minMatches; + return this; + } + + /** + * Set the maximum amount of matches allowed. Defaults to {@link Integer#MAX_VALUE}. Must + * be greater than or equal to {@linkplain #minMatches(int)}. + * + * @param maxMatches the maximum amount of matches allowed + * @return this builder, for chaining + */ + public Builder maxMatches(int maxMatches) { + this.maxMatches = maxMatches; + return this; + } + + /** + * Set the offsets to test. Defaults to all {@linkplain Direction.Flag#CARDINAL cardinal} + * and {@linkplain Direction.Flag#UPRIGHT upright} directions. + * + * @param offsets the offsets to test + * @return this builder, for chaining + */ + public Builder offsets(Iterable offsets) { + this.offsets = ImmutableList.copyOf(offsets); + return this; + } + + /** + * Build an offsets mask. + * + * @return the new mask + */ + public OffsetsMask build() { + return new OffsetsMask(mask, excludeSelf, minMatches, maxMatches, offsets); + } + } + private final Mask mask; private final boolean excludeSelf; private final int minMatches; private final int maxMatches; private final ImmutableList offsets; - /** - * Create an OffsetsMask for a single offset. - * - * @param mask The mask to use - * @param offset The offset - * @return The offsets mask - */ - public static OffsetsMask single(Mask mask, BlockVector3 offset) { - return new OffsetsMask(mask, false, 1, 1, ImmutableList.of(offset)); - } - - /** - * Create a new instance using all adjacent faces excluding diagonals. - * - *

- * Note: This passes as long as there is at least one match, and does not - * exclude cases where the block being checked matches the mask. - *

- * - * @param mask the mask to test against - */ - public OffsetsMask(Mask mask) { - this(mask, false); - } - - /** - * Create a new instance using all adjacent faces excluding diagonals. - * - *

- * Note: This passes as long as there is at least one match. - *

- * - * @param mask the mask to test against - * @param excludeSelf excludes blocks where the mask matches itself - */ - public OffsetsMask(Mask mask, boolean excludeSelf) { - this(mask, excludeSelf, 1, Integer.MAX_VALUE); - } - - /** - * Create a new instance using all adjacent faces excluding diagonals. - * - * @param mask the mask to test against - * @param excludeSelf excludes blocks where the mask matches itself - * @param minMatches the minimum number of matches (inclusive) - * @param maxMatches the maximum number of matches (inclusive) - */ - public OffsetsMask(Mask mask, boolean excludeSelf, int minMatches, int maxMatches) { - this(mask, excludeSelf, minMatches, maxMatches, OFFSET_LIST); - } - - /** - * Create a new instance. - * - *

- * Note: the minimum number of matches must be 0 or greater, and - * the maximum number of matches cannot be below the minimum. - *

- * - * @param mask the mask to test against - * @param excludeSelf excludes blocks where the mask matches itself - * @param minMatches the minimum number of matches (inclusive) - * @param maxMatches the maximum number of matches (inclusive) - * @param offsets the block offsets to test with - */ - public OffsetsMask(Mask mask, boolean excludeSelf, int minMatches, int maxMatches, List offsets) { + private OffsetsMask(Mask mask, boolean excludeSelf, int minMatches, int maxMatches, ImmutableList offsets) { checkNotNull(mask); checkNotNull(offsets); // Validate match args. No need to test maxMatches as it must be >=0 based on the conditions here. @@ -126,7 +163,7 @@ public OffsetsMask(Mask mask, boolean excludeSelf, int minMatches, int maxMatche this.excludeSelf = excludeSelf; this.minMatches = minMatches; this.maxMatches = maxMatches; - this.offsets = ImmutableList.copyOf(offsets); + this.offsets = offsets; } /** @@ -138,15 +175,6 @@ public Mask getMask() { return mask; } - /** - * Get the offsets. - * - * @return the offsets - */ - public ImmutableList getOffsets() { - return this.offsets; - } - /** * Get the flag determining if matching the current block should fail the mask. * @@ -174,6 +202,15 @@ public int getMaxMatches() { return this.maxMatches; } + /** + * Get the offsets. + * + * @return the offsets + */ + public ImmutableList getOffsets() { + return this.offsets; + } + @Override public boolean test(BlockVector3 vector) { if (excludeSelf && mask.test(vector)) { @@ -199,13 +236,12 @@ public boolean test(BlockVector3 vector) { public Mask2D toMask2D() { Mask2D childMask = getMask().toMask2D(); if (childMask != null) { - return new OffsetsMask2D( - childMask, - excludeSelf, - minMatches, - maxMatches, - getOffsets().stream().map(BlockVector3::toBlockVector2).collect(Collectors.toList()) - ); + return OffsetsMask2D.builder(childMask) + .excludeSelf(excludeSelf) + .minMatches(minMatches) + .maxMatches(maxMatches) + .offsets(Lists.transform(offsets, BlockVector3::toBlockVector2)) + .build(); } else { return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetsMask2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetsMask2D.java index e81afef0d..ba8c34d7c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetsMask2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetsMask2D.java @@ -43,64 +43,115 @@ public class OffsetsMask2D extends AbstractMask2D { .collect(Collectors.toList()) ); + /** + * Create an offsets mask for a single offset. + * + * @param mask the mask to use + * @param offset the offset + * @return the new offsets mask + */ + public static OffsetsMask2D single(Mask2D mask, BlockVector2 offset) { + return builder(mask).maxMatches(1).offsets(ImmutableList.of(offset)).build(); + } + + /** + * Create a new builder, using the given mask. + * @param mask the mask to use + * @return the builder + */ + public static Builder builder(Mask2D mask) { + return new Builder().mask(mask); + } + + /** + * A builder for an {@link OffsetsMask}. + */ + public static final class Builder { + private Mask2D mask; + private boolean excludeSelf; + private int minMatches = 1; + private int maxMatches = Integer.MAX_VALUE; + private ImmutableList offsets = OFFSET_LIST; + + private Builder() { + } + + /** + * Set the mask to test. + * @param mask the mask to test + * @return this builder, for chaining + */ + public Builder mask(Mask2D mask) { + this.mask = mask; + return this; + } + + /** + * Set whether the mask should fail if the original position matches. Defaults to + * {@code false}. + * + * @param excludeSelf {@code true} to exclude the original position if it matches + * @return this builder, for chaining + */ + public Builder excludeSelf(boolean excludeSelf) { + this.excludeSelf = excludeSelf; + return this; + } + + /** + * Set the minimum amount of matches required. Defaults to {@code 1}. Must be smaller than + * or equal to the {@linkplain #maxMatches(int) max matches} and the {@link #offsets} size, + * and greater than or equal to {@code 0}. + * + * @param minMatches the minimum amount of matches required + * @return this builder, for chaining + */ + public Builder minMatches(int minMatches) { + this.minMatches = minMatches; + return this; + } + + /** + * Set the maximum amount of matches allowed. Defaults to {@link Integer#MAX_VALUE}. Must + * be greater than or equal to {@linkplain #minMatches(int)}. + * + * @param maxMatches the maximum amount of matches allowed + * @return this builder, for chaining + */ + public Builder maxMatches(int maxMatches) { + this.maxMatches = maxMatches; + return this; + } + + /** + * Set the offsets to test. Defaults to all {@linkplain Direction.Flag#CARDINAL cardinal} + * directions. + * + * @param offsets the offsets to test + * @return this builder, for chaining + */ + public Builder offsets(Iterable offsets) { + this.offsets = ImmutableList.copyOf(offsets); + return this; + } + + /** + * Build an offsets mask. + * + * @return the new mask + */ + public OffsetsMask2D build() { + return new OffsetsMask2D(mask, excludeSelf, minMatches, maxMatches, offsets); + } + } + private final Mask2D mask; private final boolean excludeSelf; private final int minMatches; private final int maxMatches; private final ImmutableList offsets; - /** - * Create an OffsetsMask2D for a single offset. - * - * @param mask The mask to use - * @param offset The offset - * @return The offsets mask - */ - public static OffsetsMask2D single(Mask2D mask, BlockVector2 offset) { - return new OffsetsMask2D(mask, false, 1, 1, ImmutableList.of(offset)); - } - - /** - * Create a new instance. - * - * @param mask the mask to test against - */ - public OffsetsMask2D(Mask2D mask) { - this(mask, false); - } - - /** - * Create a new instance. - * - * @param mask the mask to test against - * @param excludeSelf excludes blocks where the mask matches itself - */ - public OffsetsMask2D(Mask2D mask, boolean excludeSelf) { - this(mask, excludeSelf, 1, Integer.MAX_VALUE); - } - - /** - * Create a new instance. - * - * @param mask the mask to test against - * @param excludeSelf excludes blocks where the mask matches itself - * @param minMatches the minimum number of matches (inclusive) - * @param maxMatches the maximum number of matches (inclusive) - */ - public OffsetsMask2D(Mask2D mask, boolean excludeSelf, int minMatches, int maxMatches) { - this(mask, excludeSelf, minMatches, maxMatches, OFFSET_LIST); - } - - /** - * Create a new instance. - * - * @param mask the mask to test against - * @param excludeSelf excludes blocks where the mask matches itself - * @param minMatches the minimum number of matches (inclusive) - * @param maxMatches the maximum number of matches (inclusive) - * @param offsets the block offsets to test with - */ - public OffsetsMask2D(Mask2D mask, boolean excludeSelf, int minMatches, int maxMatches, List offsets) { + private OffsetsMask2D(Mask2D mask, boolean excludeSelf, int minMatches, int maxMatches, ImmutableList offsets) { checkNotNull(mask); checkNotNull(offsets); // Validate match args. No need to test maxMatches as it must be >=0 based on the conditions here. @@ -113,7 +164,7 @@ public OffsetsMask2D(Mask2D mask, boolean excludeSelf, int minMatches, int maxMa this.excludeSelf = excludeSelf; this.minMatches = minMatches; this.maxMatches = maxMatches; - this.offsets = ImmutableList.copyOf(offsets); + this.offsets = offsets; } /** @@ -125,15 +176,6 @@ public Mask2D getMask() { return mask; } - /** - * Get the offsets. - * - * @return the offsets - */ - public List getOffsets() { - return this.offsets; - } - /** * Get the flag determining if matching the current block should fail the mask. * @@ -161,6 +203,15 @@ public int getMaxMatches() { return this.maxMatches; } + /** + * Get the offsets. + * + * @return the offsets + */ + public ImmutableList getOffsets() { + return this.offsets; + } + @Override public boolean test(BlockVector2 vector) { if (excludeSelf && mask.test(vector)) {