diff options
17 files changed, 1331 insertions, 127 deletions
diff --git a/projects/net.wotonomy.datastore/src/main/java/net/wotonomy/datastore/DataKey.java b/projects/net.wotonomy.datastore/src/main/java/net/wotonomy/datastore/DataKey.java index 4ac8b50..100800f 100644 --- a/projects/net.wotonomy.datastore/src/main/java/net/wotonomy/datastore/DataKey.java +++ b/projects/net.wotonomy.datastore/src/main/java/net/wotonomy/datastore/DataKey.java @@ -22,13 +22,17 @@ import java.io.Serializable; import net.wotonomy.foundation.internal.ValueConverter; -public class DataKey implements Comparable, Serializable, Cloneable { +/** + * Represents an abstract key for a piece of data + * + */ +public class DataKey implements Comparable<Object>, Serializable, Cloneable { static final long serialVersionUID = 8421127539579065705L; Long key; public DataKey() { - key = new Long(0); + key = 0L; } /** @@ -39,18 +43,21 @@ public class DataKey implements Comparable, Serializable, Cloneable { setKeyString(aString); } + @Override public int hashCode() { return key.intValue(); } public void increment() { - key = new Long(key.longValue() + 1); + key = Long.valueOf(key.longValue() + 1); } + @Override public Object clone() { return new DataKey(this.toString()); } + @Override public String toString() { return key.toString(); } @@ -65,6 +72,7 @@ public class DataKey implements Comparable, Serializable, Cloneable { key = parsed; } + @Override public boolean equals(Object anObject) { if (anObject instanceof String) { if (toString().equals(anObject)) { @@ -76,6 +84,7 @@ public class DataKey implements Comparable, Serializable, Cloneable { return key.equals(((DataKey) anObject).key); } + @Override public int compareTo(Object anObject) { if (anObject instanceof String) { if (toString().equals(anObject)) { diff --git a/projects/net.wotonomy.foundation/pom.xml b/projects/net.wotonomy.foundation/pom.xml index edb26be..4a347cc 100644 --- a/projects/net.wotonomy.foundation/pom.xml +++ b/projects/net.wotonomy.foundation/pom.xml @@ -13,7 +13,7 @@ <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> - <version>3.8.1</version> + <version>4.13.1</version> <scope>test</scope> </dependency> <dependency> diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSData.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSData.java index 3120ad3..ca112c9 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSData.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSData.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; /** * A pure java implementation of NSData, which is basically a wrapper on a byte @@ -214,6 +215,18 @@ public class NSData { return false; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(bytes); + return result; + } + + @Override + public boolean equals(Object obj) { + return isEqual(obj); + } } /* diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDate.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDate.java index e3ea753..7a097e4 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDate.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSDate.java @@ -18,6 +18,7 @@ License along with this library; if not, see http://www.gnu.org package net.wotonomy.foundation; +import java.time.Instant; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; @@ -49,6 +50,14 @@ public class NSDate extends Date { } /** + * Create an NSDate that represents the given instant + * @param inst The instant + */ + public NSDate(Instant inst) { + super(inst.toEpochMilli()); + } + + /** * Represents the specified number of seconds from the current date. */ public NSDate(double seconds) { diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java new file mode 100644 index 0000000..69714c2 --- /dev/null +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java @@ -0,0 +1,237 @@ +package net.wotonomy.foundation; + +import java.util.*; +import java.util.function.*; + +/** + * Represents a choice between objects of two types + * + * @author bjculkin + * + * @param <LeftType> The type that could be on the left. + * + * @param <RightType> The type that could be on the right. + * + */ +public class NSEither<LeftType, RightType> { + /** + * Create a new either with the left value occupied. + * + * @param <LeftType> The type of the left value. + * + * @param <RightType> The type of the empty right value. + * + * @param left The value to put on the left. + * + * @return An either with the left side occupied. + */ + public static <LeftType, RightType> NSEither<LeftType, RightType> left(final LeftType left) { + return new NSEither<>(left, null); + } + + /** + * Create a new either with the right value occupied. + * + * @param <LeftType> The type of the empty left value. + * + * @param <RightType> The type of the right value. + * + * @param right The value to put on the right. + * + * @return An either with the right side occupied. + */ + public static <LeftType, RightType> NSEither<LeftType, RightType> right(final RightType right) { + return new NSEither<>(null, right); + } + + /* The left value of the either. */ + private LeftType leftVal; + /* The right value of the either. */ + private RightType rightVal; + /* Whether the left value is the one filled out. */ + private boolean isLeft; + + /* Create a new either with specifed values. */ + private NSEither(final LeftType left, final RightType right) { + if (left == null) { + rightVal = right; + } else { + leftVal = left; + + isLeft = true; + } + } + + /** + * Perform a mapping over this either. + * + * @param <NewLeft> The new left type. + * @param <NewRight> The new right type. + * + * @param leftFunc The function to apply if this is a left either. + * @param rightFunc The function to apply if this is a right either. + * + * @return A new either, containing a value transformed by the appropriate + * function. + */ + public <NewLeft, NewRight> NSEither<NewLeft, NewRight> map(Function<LeftType, NewLeft> leftFunc, + Function<RightType, NewRight> rightFunc) { + return isLeft ? left(leftFunc.apply(leftVal)) : right(rightFunc.apply(rightVal)); + } + + /** + * Extract the value from this Either. + * + * @param <Common> The common type to extract. + * + * @param leftHandler The function to handle left-values. + * @param rightHandler The function to handle right-values. + * + * @return The result of applying the proper function. + */ + public <Common> Common extract(Function<LeftType, Common> leftHandler, Function<RightType, Common> rightHandler) { + return isLeft ? leftHandler.apply(leftVal) : rightHandler.apply(rightVal); + } + + /** + * Perform an action on this either. + * + * @param leftHandler The handler of left values. + * @param rightHandler The handler of right values. + */ + public void pick(Consumer<LeftType> leftHandler, Consumer<RightType> rightHandler) { + if (isLeft) + leftHandler.accept(leftVal); + else + rightHandler.accept(rightVal); + } + + /** + * Check if this either is left-aligned (has the left value filled, not the + * right value). + * + * @return Whether this either is left-aligned. + */ + public boolean isLeft() { + return isLeft; + } + + /** + * Get the left value of this either if there is one. + * + * @return An optional containing the left value, if there is one. + */ + public Optional<LeftType> getLeft() { + return Optional.ofNullable(leftVal); + } + + /** + * Get the left value of this either, or get a {@link NoSuchElementException} if + * there isn't one. + * + * @return The left value of this either. + * + * @throws NoSuchElementException If this either doesn't have a left value. + */ + public LeftType forceLeft() { + if (isLeft) { + return leftVal; + } + + throw new NoSuchElementException("Either has no left value, is right value"); + } + + /** + * Get the right value of this either if there is one. + * + * @return An optional containing the right value, if there is one. + */ + public Optional<RightType> getRight() { + return Optional.ofNullable(rightVal); + } + + /** + * Get the right value of this either, or get a {@link NoSuchElementException} + * if there isn't one. + * + * @return The right value of this either. + * + * @throws NoSuchElementException If this either doesn't have a right value. + */ + public RightType forceRight() { + if (isLeft) { + throw new NoSuchElementException("Either has no right value, has left value"); + } + + return rightVal; + } + + /** + * Change the type of the right-side of this either. + * + * Works only for left Eithers. + * + * @param <T> The new type for the right side + * @return The either with the new type. + */ + @SuppressWarnings("unchecked") + public <T> NSEither<LeftType, T> newRight() { + if (isLeft) return (NSEither<LeftType, T>) this; + + throw new NoSuchElementException("Can't replace right type on right Either"); + } + + /** + * Change the type of the left-side of this either. + * + * Works only for right Eithers. + * + * @param <T> The new type for the left side + * @return The either with the new type. + */ + @SuppressWarnings("unchecked") + public <T> NSEither<T, RightType> newLeft() { + if (isLeft) + throw new NoSuchElementException("Can't replace left type on left Either"); + return (NSEither<T, RightType>) this; + } + + /** + * Collapse an Either with the same type on both sides. + * + * @param <T> The type of the either + * @param eth The either to collapse + * + * @return The collapsed either + */ + public static <T> T collapse(NSEither<T, T> eth) { + Function<T, T> id = (x) -> x; + return eth.extract(id, id); + } + // Misc. overrides + + @Override + public int hashCode() { + return Objects.hash(isLeft, leftVal, rightVal); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + + NSEither<?, ?> other = (NSEither<?, ?>) obj; + + return isLeft == other.isLeft && Objects.equals(leftVal, other.leftVal) + && Objects.equals(rightVal, other.rightVal); + } + + @Override + public String toString() { + return String.format("Either [leftVal='%s', rightVal='%s', isLeft=%s]", leftVal, rightVal, isLeft); + } +} diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSError.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSError.java new file mode 100644 index 0000000..9e46b5e --- /dev/null +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSError.java @@ -0,0 +1,71 @@ +package net.wotonomy.foundation; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Represents an error code. + * + * @author bjculkin + * + */ +public class NSError implements Serializable { + private static final long serialVersionUID = 532874201592029465L; + + public static final String NSWotonomyDomain = "wotonomy"; + public static final String NSJavaDomain = "java"; + + public final String domain; + public final int error; + + public final NSDictionary<String, Object> userInfo; + + private String description; + + private NSArray<NSError> underlyingErrors = new NSMutableArray<>(); + + @SuppressWarnings("unchecked") + public NSError(String domain, int error) { + this(domain, error, (NSDictionary<String, Object>) NSDictionary.EmptyDictionary); + } + + public NSError(String domain, int error, NSDictionary<String, Object> userInfo) { + this.domain = domain; + this.error = error; + this.userInfo = userInfo; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public NSArray<NSError> getUnderlyingErrors() { + return underlyingErrors; + } + + public void addUnderlyingError(NSError underlying) { + underlyingErrors.add(underlying); + } + + @Override + public int hashCode() { + return Objects.hash(domain, error, userInfo, description); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NSError other = (NSError) obj; + return Objects.equals(domain, other.domain) && error == other.error && Objects.equals(userInfo, other.userInfo) + && Objects.equals(description, other.description); + } +} diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyList.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyList.java new file mode 100644 index 0000000..a8cbd3d --- /dev/null +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyList.java @@ -0,0 +1,367 @@ +package net.wotonomy.foundation; + +import java.io.Serializable; +import java.util.Objects; + +public abstract class NSPropertyList implements Serializable { + private static final long serialVersionUID = 2671697049722442864L; + + public static enum Type { + ARRAY, DICTIONARY, STRING, DATA, DATE, + INTEGER, REAL, BOOL + } + + public final Type type; + + protected boolean isImmutable; + + private NSPropertyList(Type type) { + this.type = type; + } + + public boolean isImmutable() { + return isImmutable; + } + + public void makeImmutable() { + this.isImmutable = true; + } + + @Override + public int hashCode() { + return Objects.hash(type); + } + + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NSPropertyList other = (NSPropertyList) obj; + return type == other.type; + } + + public static final class Array extends NSPropertyList { + private static final long serialVersionUID = 7386250174020490701L; + + private NSArray<NSPropertyList> contents; + + public Array(NSArray<NSPropertyList> contents) { + super(Type.ARRAY); + + this.contents = contents; + } + + public NSArray<NSPropertyList> getContents() { + return contents; + } + + public boolean setContents(NSArray<NSPropertyList> contents) { + if (this.isImmutable) return false; + + this.contents = contents; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(contents); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Array other = (Array) obj; + return Objects.equals(contents, other.contents); + } + } + + public static final class Dictionary extends NSPropertyList { + private static final long serialVersionUID = 1979462360377516540L; + + private NSDictionary<java.lang.String, NSPropertyList> contents; + + public Dictionary(NSDictionary<java.lang.String,NSPropertyList> retList) { + super(Type.DICTIONARY); + + this.contents = retList; + } + + public NSDictionary<java.lang.String, NSPropertyList> getContents() { + return contents; + } + + public boolean setContents(NSDictionary<java.lang.String, NSPropertyList> contents) { + if (this.isImmutable) return false; + + this.contents = contents; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(contents); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Dictionary other = (Dictionary) obj; + return Objects.equals(contents, other.contents); + } + } + + public static final class String extends NSPropertyList { + private static final long serialVersionUID = -388698351414802814L; + + private java.lang.String contents; + + public String(java.lang.String contents) { + super(Type.STRING); + + this.contents = contents; + } + + public java.lang.String getContents() { + return contents; + } + + public boolean setContents(java.lang.String contents) { + if (this.isImmutable) return false; + + this.contents = contents; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(contents); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + String other = (String) obj; + return Objects.equals(contents, other.contents); + } + } + + public static final class Data extends NSPropertyList { + private static final long serialVersionUID = -6866410755763823986L; + private NSData contents; + + public Data(NSData contents) { + super(Type.DATA); + + this.contents = contents; + } + + public NSData getContents() { + return contents; + } + + public boolean setContents(NSData contents) { + if (this.isImmutable) return false; + + this.contents = contents; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(contents); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Data other = (Data) obj; + return Objects.equals(contents, other.contents); + } + } + + public static final class Date extends NSPropertyList { + private static final long serialVersionUID = 6245107872338103662L; + + private NSDate contents; + + public Date(NSDate contents) { + super(Type.DATE); + + this.contents = contents; + } + + public NSDate getContents() { + return contents; + } + + public boolean setContents(NSDate contents) { + if (this.isImmutable) return false; + + this.contents = contents; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(contents); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Date other = (Date) obj; + return Objects.equals(contents, other.contents); + } + } + + public static final class Integer extends NSPropertyList { + private static final long serialVersionUID = -6375080842293791774L; + + private int contents; + + public Integer(int contents) { + super(Type.INTEGER); + + this.contents = contents; + } + + public int getContents() { + return contents; + } + + public boolean setContents(int contents) { + if (this.isImmutable) return false; + + this.contents = contents; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(contents); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Integer other = (Integer) obj; + return contents == other.contents; + } + } + + public static final class Real extends NSPropertyList { + private static final long serialVersionUID = -4548471713243500294L; + + private double contents; + + public Real(double contents) { + super(Type.REAL); + + this.contents = contents; + } + + public double getContents() { + return contents; + } + + public boolean setContents(double contents) { + if (this.isImmutable) return false; + + this.contents = contents; + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(contents); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Real other = (Real) obj; + return Double.doubleToLongBits(contents) == Double.doubleToLongBits(other.contents); + } + } + + public static final class Bool extends NSPropertyList { + private static final long serialVersionUID = 1169221814850684398L; + private boolean bool; + + public Bool(boolean val) { + super(Type.BOOL); + } + + public boolean getValue() { + return bool; + } + + public boolean setValue(boolean bool) { + if (this.isImmutable) return false; + + this.bool = bool; + return true; + } + } +} diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyListSerialization.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyListSerialization.java index b819662..01a0445 100644 --- a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyListSerialization.java +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSPropertyListSerialization.java @@ -3,8 +3,6 @@ package net.wotonomy.foundation; /** * Class for serializing/unserializing property lists in the .plist format - * - * */ public class NSPropertyListSerialization { diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSXMLPropertyList.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSXMLPropertyList.java new file mode 100644 index 0000000..6881d71 --- /dev/null +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSXMLPropertyList.java @@ -0,0 +1,363 @@ +package net.wotonomy.foundation; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.util.Base64; +import java.util.Base64.Decoder; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.*; +import org.xml.sax.SAXException; + +import net.wotonomy.foundation.internal.ReaderInputStream; +import net.wotonomy.foundation.internal.WotonomyException; + +/** + * Allows reading/writing property lists in the newer XML format instead of + * the older text-based one. + * + * @author bjculkin + * + */ +public class NSXMLPropertyList { + public static enum ErrorCodes { + INVALID_ROOT("Invalid root element, must be <plist>"), + BAD_PLIST_CHILD_COUNT("<%s> must have one child element", 1), + BAD_PLIST_CHILD("child of <%s> must be a %s"), + UNKNOWN_NODE_TYPE("unknown node type %s", 1), + INVALID_NUM("'%s' is not a valid %s", 2), + BAD_COLL_ITEM("Encountered error while parsing %s", 1), + MISSING_MAP_KEY("encountered a <%s> while parsing a map, expected <key>", 1); + + public final String desc; + public final int numFormatArgs; + + private ErrorCodes(String desc) { + this(desc, 0); + } + + private ErrorCodes(String desc, int numFormatArgs) { + this.desc = desc; + this.numFormatArgs = numFormatArgs; + } + } + + public static enum ImmutabilityType { + Immutable, MutableContainers, MutableContainersAndLeaves + } + /** + * Parse a property list from a given string, with the property list + * being immutable. + * + * @param s The string to parse the property list from + * @return The immutable property list + */ + public static NSEither<NSPropertyList, NSError> propertyListFromString(String s) { + return propertyListFromString(s, ImmutabilityType.Immutable); + } + + public static NSEither<NSPropertyList, NSError> propertyListFromString(String s, ImmutabilityType immutable) { + StringReader sReader = new StringReader(s); + ReaderInputStream inp = new ReaderInputStream(sReader); + return propertyListFromStream(inp, immutable); + } + + public static NSEither<NSPropertyList, NSError> propertyListFromStream(InputStream inp) { + return propertyListFromStream(inp, ImmutabilityType.Immutable); + } + + public static NSEither<NSPropertyList, NSError> propertyListFromStream(InputStream inp, ImmutabilityType immutable) { + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + // TODO allow attaching comments to each element + factory.setIgnoringComments(true); + DocumentBuilder fact = factory.newDocumentBuilder(); + Document doc = fact.parse(inp); + Element plistRoot = doc.getDocumentElement(); + plistRoot.normalize(); + + if (!plistRoot.getTagName().equals("plist")) { + return createError(ErrorCodes.INVALID_ROOT); + } + + NodeList children = plistRoot.getChildNodes(); + // TODO this needs to be adjusted to allow comments to exist + if (children.getLength() != 1) { + return createError(ErrorCodes.BAD_PLIST_CHILD_COUNT, "plist"); + } + + Node initial = children.item(0); + if (initial.getNodeType() != Node.ELEMENT_NODE) { + return createError(ErrorCodes.BAD_PLIST_CHILD, "plist", "tag"); + } + + return parsePlistValue(initial, immutable); + } catch (ParserConfigurationException pcex) { + throw new WotonomyException("Failed to create parser", pcex); + } catch (SAXException saxex) { + throw new RuntimeException("Failed to read XML", saxex); + } catch (IOException ioex) { + throw new RuntimeException("Error parsing XML", ioex); + } + } + + private static NSEither<NSPropertyList, NSError> parsePlistValue(Node nod, ImmutabilityType immutable) { + String initNodeName = nod.getNodeName(); + switch(initNodeName) { + case "array": + return arrayFromNode(nod, immutable); + case "dict": + return dictFromNode(nod, immutable); + case "string": { + NodeList stringKid = nod.getChildNodes(); + if (stringKid.getLength() != 1) return createError(ErrorCodes.BAD_PLIST_CHILD_COUNT, "string"); + Node text = stringKid.item(0); + if (text.getNodeType() != Node.TEXT_NODE) return createError(ErrorCodes.BAD_PLIST_CHILD, "string", "text"); + NSPropertyList.String ret = new NSPropertyList.String(text.getNodeValue()); + if (immutable != ImmutabilityType.MutableContainersAndLeaves) ret.makeImmutable(); + return NSEither.left(ret); + } + + case "data": { + Decoder decoder = Base64.getDecoder(); + NodeList stringKid = nod.getChildNodes(); + if (stringKid.getLength() != 1) return createError(ErrorCodes.BAD_PLIST_CHILD_COUNT, "data"); + Node text = stringKid.item(0); + if (text.getNodeType() != Node.TEXT_NODE) return createError(ErrorCodes.BAD_PLIST_CHILD, "data", "text"); + String raw = text.getNodeValue(); + NSData dat = new NSData(decoder.decode(raw)); + NSPropertyList.Data ret = new NSPropertyList.Data(dat); + if (immutable != ImmutabilityType.MutableContainersAndLeaves) ret.makeImmutable(); + return NSEither.left(ret); + } + case "date": { + // TODO determine what the correct default date format is + // Believe it is ISO_INSTANT from the documentation I have read + DateTimeFormatter dateFmt = DateTimeFormatter.ISO_INSTANT; + NamedNodeMap attributes = nod.getAttributes(); + Node formatNode = attributes.getNamedItem("format"); + // Because we got it from the attribute map, it has to be + // an attribute + if (formatNode != null) { + Attr formatAttr = (Attr) formatNode; + String format = formatAttr.getValue(); + dateFmt = DateTimeFormatter.ofPattern(format); + } + + // TODO abstract this chunk into a method + NodeList stringKid = nod.getChildNodes(); + if (stringKid.getLength() != 1) return createError(ErrorCodes.BAD_PLIST_CHILD_COUNT, "date"); + Node text = stringKid.item(0); + if (text.getNodeType() != Node.TEXT_NODE) return createError(ErrorCodes.BAD_PLIST_CHILD, "date", "text"); + String val = text.getNodeValue(); + + TemporalAccessor tempDate = dateFmt.parse(val); + Instant inst = Instant.from(tempDate); + + NSPropertyList.Date ret = new NSPropertyList.Date(new NSDate(inst)); + if (immutable != ImmutabilityType.MutableContainersAndLeaves) ret.makeImmutable(); + return NSEither.left(ret); + } + case "integer": { + NodeList stringKid = nod.getChildNodes(); + if (stringKid.getLength() != 1) return createError(ErrorCodes.BAD_PLIST_CHILD_COUNT, "integer"); + Node text = stringKid.item(0); + if (text.getNodeType() != Node.TEXT_NODE) return createError(ErrorCodes.BAD_PLIST_CHILD, "integer", "text"); + String val = text.getNodeValue(); + + try { + int ret = Integer.parseInt(val); + + NSPropertyList.Integer res = new NSPropertyList.Integer(ret); + if (immutable != ImmutabilityType.MutableContainersAndLeaves) res.makeImmutable(); + return NSEither.left(res); + } catch (NumberFormatException nfex) { + return createError(ErrorCodes.INVALID_NUM, val, "integer"); + } + } + case "real": { + NodeList stringKid = nod.getChildNodes(); + if (stringKid.getLength() != 1) return createError(ErrorCodes.BAD_PLIST_CHILD_COUNT, "real"); + Node text = stringKid.item(0); + if (text.getNodeType() != Node.TEXT_NODE) return createError(ErrorCodes.BAD_PLIST_CHILD, "real", "text"); + String val = text.getNodeValue(); + + try { + double ret = Double.parseDouble(val); + + NSPropertyList.Real res = new NSPropertyList.Real(ret); + if (immutable != ImmutabilityType.MutableContainersAndLeaves) res.makeImmutable(); + return NSEither.left(res); + } catch (NumberFormatException nfex) { + return createError(ErrorCodes.INVALID_NUM, val, "double"); + } + } + case "true": { + NSPropertyList.Bool res = new NSPropertyList.Bool(true); + if (immutable != ImmutabilityType.MutableContainersAndLeaves) res.makeImmutable(); + return NSEither.left(res); + } + case "false": { + NSPropertyList.Bool res = new NSPropertyList.Bool(false); + if (immutable != ImmutabilityType.MutableContainersAndLeaves) res.makeImmutable(); + return NSEither.left(res); + } + default: + return createError(ErrorCodes.UNKNOWN_NODE_TYPE, initNodeName); + } + } + + private static NSEither<NSPropertyList, NSError> dictFromNode(Node nod, ImmutabilityType immutable) { + NodeList lst = nod.getChildNodes(); + int numChild = lst.getLength(); + + boolean hasError = false; + NSDictionary<String, NSPropertyList> retDict = new NSMutableDictionary<>(); + NSError retError = createRawError(ErrorCodes.BAD_COLL_ITEM, "dictionary"); + + for (int i = 0; i < numChild; i++) { + Node kid = lst.item(i); + short typ = kid.getNodeType(); + if (typ == Node.COMMENT_NODE || typ == Node.PROCESSING_INSTRUCTION_NODE) continue; + + if (typ != Node.ELEMENT_NODE) { + hasError = true; + // Skip over the corresponding value, since we don't have a key for it + i++; + retError.addUnderlyingError(createRawError(ErrorCodes.BAD_PLIST_CHILD, "dict", "tag")); + continue; + } + + // First, grab the key, then we can handle the value + if (!kid.getNodeName().equals("key")) { + hasError = true; + // Skip over the corresponding value, since we don't have a key for it + i++; + retError.addUnderlyingError(createRawError(ErrorCodes.MISSING_MAP_KEY, kid.getNodeName())); + continue; + } + + NodeList stringKid = kid.getChildNodes(); + + if (stringKid.getLength() != 1) { + hasError = true; + // Skip over the corresponding value, since we don't have a key for it + i++; + retError.addUnderlyingError(createRawError(ErrorCodes.BAD_PLIST_CHILD_COUNT, "key")); + continue; + } + + Node text = stringKid.item(0); + + if (text.getNodeType() != Node.TEXT_NODE) { + hasError = true; + // Skip over the corresponding value, since we don't have a key for it + i++; + retError.addUnderlyingError(createRawError(ErrorCodes.BAD_PLIST_CHILD, "key", "text")); + continue; + } + + String key = text.getNodeValue(); + i++; + + kid = lst.item(i); + typ = kid.getNodeType(); + if (typ == Node.COMMENT_NODE || typ == Node.PROCESSING_INSTRUCTION_NODE) continue; + + if (typ != Node.ELEMENT_NODE) { + hasError = true; + retError.addUnderlyingError(createRawError(ErrorCodes.BAD_PLIST_CHILD, "array", "tag")); + continue; + } + + var res = parsePlistValue(kid, immutable); + if (res.isLeft()) { + retDict.put(key, res.forceLeft()); + } else { + hasError = true; + retError.addUnderlyingError(res.forceRight()); + } + } + + + if (hasError) { + return NSEither.right(retError); + } else { + NSPropertyList.Dictionary res; + if (immutable == ImmutabilityType.Immutable) { + res = new NSPropertyList.Dictionary(new NSDictionary<>(retDict)); + res.makeImmutable(); + } else { + res = new NSPropertyList.Dictionary(new NSDictionary<>(retDict)); + } + return NSEither.left(res); + } + } + + private static NSEither<NSPropertyList, NSError> arrayFromNode(Node nod, ImmutabilityType immutable) { + NodeList lst = nod.getChildNodes(); + int numChild = lst.getLength(); + + boolean hasError = false; + NSArray<NSPropertyList> retList = new NSMutableArray<>(); + NSError retError = createRawError(ErrorCodes.BAD_COLL_ITEM, "array"); + + for (int i = 0; i < numChild; i++) { + Node kid = lst.item(i); + short typ = kid.getNodeType(); + if (typ == Node.COMMENT_NODE || typ == Node.PROCESSING_INSTRUCTION_NODE) continue; + + if (typ != Node.ELEMENT_NODE) { + hasError = true; + retError.addUnderlyingError(createRawError(ErrorCodes.BAD_PLIST_CHILD, "array", "tag")); + continue; + } + + var res = parsePlistValue(kid, immutable); + if (res.isLeft()) { + retList.add(res.forceLeft()); + } else { + hasError = true; + retError.addUnderlyingError(res.forceRight()); + } + } + + if (hasError) { + return NSEither.right(retError); + } else { + NSPropertyList.Array arr; + if (immutable == ImmutabilityType.Immutable) { + arr = new NSPropertyList.Array(new NSArray<>(retList)); + arr.makeImmutable(); + } else { + arr = new NSPropertyList.Array(retList); + } + return NSEither.left(arr); + } + } + + private static NSEither<NSPropertyList, NSError> createError(ErrorCodes errorCode, Object... formatArgs) { + NSError error = createRawError(errorCode, formatArgs); + + return NSEither.right(error); + } + + private static NSError createRawError(ErrorCodes errorCode, Object... formatArgs) { + if (formatArgs.length != errorCode.numFormatArgs) { + String fmt = "Incorrect number of format args for error code %s; got %d, expected %d"; + String msg = String.format(fmt, errorCode.name(), formatArgs.length, errorCode.numFormatArgs); + throw new WotonomyException(msg); + } + + NSError error = new NSError(NSError.NSWotonomyDomain, errorCode.ordinal()); + error.setDescription(String.format(errorCode.desc, formatArgs)); + return error; + } +} diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ReaderInputStream.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ReaderInputStream.java new file mode 100644 index 0000000..48f8b55 --- /dev/null +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/internal/ReaderInputStream.java @@ -0,0 +1,19 @@ +package net.wotonomy.foundation.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +public class ReaderInputStream extends InputStream { + private Reader rdr; + + public ReaderInputStream(Reader rdr) { + this.rdr = rdr; + } + + @Override + public int read() throws IOException { + return rdr.read(); + } + +} diff --git a/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/AllTests.java b/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/AllTests.java deleted file mode 100644 index 7a2e664..0000000 --- a/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/AllTests.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.wotonomy.foundation; - -import junit.framework.Test; -import junit.framework.TestSuite; - -public class AllTests { - - public static Test suite() { - TestSuite suite = new TestSuite("Test for net.wotonomy.foundation"); - // $JUnit-BEGIN$ - suite.addTestSuite(NSArrayTest.class); - suite.addTestSuite(NSBundleTest.class); - // $JUnit-END$ - return suite; - } - -} diff --git a/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/NSArrayTest.java b/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/NSArrayTest.java index 98689dc..bcee135 100644 --- a/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/NSArrayTest.java +++ b/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/NSArrayTest.java @@ -1,41 +1,51 @@ package net.wotonomy.foundation; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.util.ArrayList; -import java.util.Collection; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; -public class NSArrayTest extends TestCase { +public class NSArrayTest { Object o1, o2, o3, o4, o5; - + + @Before public void setUp() throws Exception { o1 = "o1"; - o2 = new Integer(2); + o2 = Integer.valueOf(2); o3 = null; o4 = "o4"; - super.setUp(); } + @After public void tearDown() throws Exception { o1 = null; o2 = null; o3 = null; o4 = null; - super.tearDown(); } /* * Test method for 'net.wotonomy.foundation.NSArray.hashCode()' */ + @Test public void testHashCode() { - NSArray array1 = new NSArray(o1); - NSArray array2 = new NSArray(o1); // same content, same hashcode. - NSArray array3 = new NSArray("Different"); + NSArray<Object> array1 = new NSArray<Object>(o1); + NSArray<Object> array2 = new NSArray<Object>(o1); // same content, same hashcode. + NSArray<String> array3 = new NSArray<String>("Different"); assertNotSame(array1, array2); assertEquals(array1.hashCode(), array2.hashCode()); assertFalse("Should have different hashcodes", array1.hashCode() == array3.hashCode()); @@ -44,9 +54,10 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.arrayBackedByList(List)' */ + @Test public void testArrayBackedByList() { List<Object> list = new ArrayList<>(); - NSArray array = NSArray.arrayBackedByList(list); + NSArray<Object> array = NSArray.arrayBackedByList(list); assertNotNull(array); assertSame(list, array.list); } @@ -54,6 +65,7 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.NSArray(List, Object)' */ + @Test public void testEmptyList() { assertNotNull(NSArray.EmptyArray); assertEquals(0, NSArray.EmptyArray.count()); @@ -62,9 +74,10 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.NSArray(List, Object)' */ + @Test public void testNSArrayListObject() { List<Object> list = new ArrayList<>(); - NSArray array = new NSArray(list, null); + NSArray<Object> array = new NSArray<Object>(list, null); assertNotNull(array); assertSame(list, array.list); } @@ -72,34 +85,37 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.NSArray(int)' */ + @Test public void testNSArrayInt() { - NSArray array = new NSArray(5); + NSArray<?> array = new NSArray<Object>(5); assertNotNull(array); - array = new NSArray(0); + array = new NSArray<Object>(0); assertNotNull(array); try { - array = new NSArray(-1); + array = new NSArray<Object>(-1); fail("Failed to catch IllegalArgumentException."); } catch (IllegalArgumentException e) { } assertNotNull(array); - array = new NSArray(1000); + array = new NSArray<Object>(1000); assertNotNull(array); } /* * Test method for 'net.wotonomy.foundation.NSArray.NSArray()' */ + @Test public void testNSArray() { - NSArray array = new NSArray(); + NSArray<?> array = new NSArray<Object>(); assertNotNull(array); } /* * Test method for 'net.wotonomy.foundation.NSArray.NSArray(Object)' */ + @Test public void testNSArrayObject() { - NSArray array = new NSArray(o1); + NSArray<Object> array = new NSArray<Object>(o1); assertNotNull(array); assertEquals(1, array.count()); assertEquals(o1, array.get(0)); @@ -108,8 +124,9 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.NSArray(Object)' */ + @Test public void testNSArrayObjectWithNull() { - NSArray array = new NSArray((Object) null); + NSArray<Object> array = new NSArray<Object>((Object) null); assertNotNull(array); assertEquals(1, array.count()); assertEquals(null, array.get(0)); @@ -118,9 +135,10 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.NSArray(Object[])' */ + @Test public void testNSArrayObjectArray() { Object[] objects = { o1, o2, o3 }; - NSArray array = new NSArray(objects); + NSArray<?> array = new NSArray<Object>(objects); assertEquals(3, array.count()); for (int i = 0; i < objects.length; i++) { assertEquals(objects[i], array.get(i)); @@ -130,12 +148,13 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.NSArray(Collection)' */ + @Test public void testNSArrayCollection() { ArrayList<Object> list = new ArrayList<>(); list.add(o1); list.add(o2); list.add(o3); - NSArray array = new NSArray(list); + NSArray<Object> array = new NSArray<Object>(list); assertNotNull(array); assertNotSame(list, array.list); assertEquals(3, array.count()); @@ -150,9 +169,10 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.arrayByAddingObject(Object)' */ + @Test public void testArrayByAddingObject() { - NSArray array = new NSArray(o1); - NSArray array2 = array.arrayByAddingObject(o2); + NSArray<Object> array = new NSArray<Object>(o1); + NSArray<Object> array2 = array.arrayByAddingObject(o2); assertEquals(2, array2.count()); assertEquals(o1, array2.get(0)); assertEquals(o2, array2.get(1)); @@ -162,12 +182,13 @@ public class NSArrayTest extends TestCase { * Test method for * 'net.wotonomy.foundation.NSArray.arrayByAddingObjectsFromArray(Collection)' */ + @Test public void testArrayByAddingObjectsFromArray() { - NSArray array = new NSArray(o1); + NSArray<Object> array = new NSArray<Object>(o1); List<Object> list = new ArrayList<>(); list.add(o2); list.add(o3); - NSArray array2 = array.arrayByAddingObjectsFromArray(list); + NSArray<Object> array2 = array.arrayByAddingObjectsFromArray(list); assertEquals(3, array2.count()); assertEquals(o1, array2.get(0)); assertEquals(o2, array2.get(1)); @@ -178,17 +199,19 @@ public class NSArrayTest extends TestCase { * Test method for * 'net.wotonomy.foundation.NSArray.componentsJoinedByString(String)' */ + @Test public void testComponentsJoinedByString() { Object[] objects = { o1, o2, o3 }; - NSArray array = new NSArray(objects); + NSArray<?> array = new NSArray<Object>(objects); assertEquals("o1, 2, null", array.componentsJoinedByString(", ")); } /* * Test method for 'net.wotonomy.foundation.NSArray.containsObject(Object)' */ + @Test public void testContainsObject() { - NSArray array = new NSArray(o1); + NSArray<Object> array = new NSArray<Object>(o1); assertTrue(array.contains(o1)); assertFalse(array.contains(o2)); } @@ -197,22 +220,24 @@ public class NSArrayTest extends TestCase { * Test method for * 'net.wotonomy.foundation.NSArray.firstObjectCommonWithArray(Collection)' */ + @Test public void testFirstObjectCommonWithArray() { ArrayList<Object> list = new ArrayList<>(); list.add(o2); list.add(o3); list.add(o4); - NSArray array = new NSArray(new Object[] { o1, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o3, o4 }); assertEquals(o3, array.firstObjectCommonWithArray(list)); } /* * Test method for 'net.wotonomy.foundation.NSArray.isEqualToArray(List)' */ + @Test public void testEqualsAndIsEqualToArray() { - NSArray array1 = new NSArray(o1); - NSArray array2 = new NSArray(o1); // same content, same hashcode. - NSArray array3 = new NSArray(o2); + NSArray<Object> array1 = new NSArray<>(o1); + NSArray<Object> array2 = new NSArray<>(o1); // same content, same hashcode. + NSArray<Object> array3 = new NSArray<>(o2); assertNotSame(array1, array2); assertTrue("Should be equal", array1.equals(array2)); assertTrue("Should be equal", array1.isEqualToArray(array2)); @@ -223,8 +248,9 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.lastObject()' */ + @Test public void testLastObject() { - NSArray array = new NSArray(new Object[] { o1, o2 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2 }); assertEquals(o2, array.lastObject()); // test for empty array assertEquals(null, NSArray.EmptyArray.lastObject()); @@ -234,9 +260,10 @@ public class NSArrayTest extends TestCase { * Test method for 'net.wotonomy.foundation.NSArray.subarrayWithRange(NSRange)' * TODO: Add ranges that exceed the size of the array. */ + @Test public void testSubarrayWithRange() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); - NSArray subarray = array.subarrayWithRange(new NSRange(1, 2)); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); + NSArray<?> subarray = array.subarrayWithRange(new NSRange(1, 2)); assertEquals(2, subarray.count()); assertEquals(o2, subarray.get(0)); assertEquals(o3, subarray.get(1)); @@ -245,9 +272,10 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.objectEnumerator()' */ + @Test public void testObjectEnumerator() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); - Enumeration e = array.objectEnumerator(); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); + Enumeration<?> e = array.objectEnumerator(); assertTrue(e.hasMoreElements()); assertEquals(o1, e.nextElement()); assertEquals(o2, e.nextElement()); @@ -258,9 +286,10 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.reverseObjectEnumerator()' */ + @Test public void testReverseObjectEnumerator() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); - Enumeration e = array.reverseObjectEnumerator(); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); + Enumeration<?> e = array.reverseObjectEnumerator(); assertTrue(e.hasMoreElements()); assertEquals(o4, e.nextElement()); assertEquals(o3, e.nextElement()); @@ -271,9 +300,10 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.getObjects(Object[])' */ + @Test public void testGetObjectsObjectArray() { Object[] oa = new Object[4]; - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); array.getObjects(oa); assertEquals(o1, oa[0]); assertEquals(o2, oa[1]); @@ -282,18 +312,20 @@ public class NSArrayTest extends TestCase { assertEquals(4, oa.length); } + @Test public void testGetObjectsObjectArrayWithSmallArray() { Object[] oa = new Object[2]; - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); array.getObjects(oa); assertEquals(o1, oa[0]); assertEquals(o2, oa[1]); assertEquals(2, oa.length); } + @Test public void testGetObjectsObjectArrayWithLargeArray() { Object[] oa = new Object[5]; - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); array.getObjects(oa); assertEquals(o1, oa[0]); assertEquals(o2, oa[1]); @@ -307,18 +339,20 @@ public class NSArrayTest extends TestCase { * Test method for 'net.wotonomy.foundation.NSArray.getObjects(Object[], * NSRange)' TODO: Try more ranges. */ + @Test public void testGetObjectsObjectArrayNSRange() { Object[] oa = new Object[2]; - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); array.getObjects(oa, new NSRange(1, 2)); assertEquals(o2, oa[0]); assertEquals(o3, oa[1]); assertEquals(2, oa.length); } + @Test public void testGetObjectsObjectArrayNSRangeWithLargeRange() { Object[] oa = new Object[4]; - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); array.getObjects(oa, new NSRange(1, 90)); assertEquals(o2, oa[0]); assertEquals(o3, oa[1]); @@ -330,8 +364,9 @@ public class NSArrayTest extends TestCase { /* * Test method for 'net.wotonomy.foundation.NSArray.indexOfObject(Object)' */ + @Test public void testIndexOfObjectObject() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); assertEquals(0, array.indexOfObject(o1)); assertEquals(1, array.indexOfObject(o2)); assertEquals(2, array.indexOfObject(o3)); @@ -343,8 +378,9 @@ public class NSArrayTest extends TestCase { * Test method for 'net.wotonomy.foundation.NSArray.indexOfObject(Object, * NSRange)' */ + @Test public void testIndexOfObjectObjectNSRange() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); NSRange range = new NSRange(1, 2); assertEquals(-1, array.indexOfObject(o1, range)); assertEquals(1, array.indexOfObject(o2, range)); @@ -355,20 +391,22 @@ public class NSArrayTest extends TestCase { assertEquals(-1, array.indexOfObject("No Such Object")); } + @Test public void testIndexOfIdenticalObjectObject() { - Integer i1 = new Integer(3 - 1); - Integer i2 = new Integer(4 / 2); - NSArray array = new NSArray(new Object[] { o1, i2, o3, o4 }); + Integer i1 = Integer.valueOf(3 - 1); + Integer i2 = Integer.valueOf(4 / 2); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, i2, o3, o4 }); assertEquals(1, array.indexOfObject(i1)); assertFalse(1 == array.indexOfIdenticalObject(i1)); assertEquals(1, array.indexOfIdenticalObject(i2)); } + @Test public void testIndexOfIdenticalObjectObjectNSRange() { - Integer i1 = new Integer(3 - 1); - Integer i2 = new Integer(4 / 2); + Integer i1 = Integer.valueOf(3 - 1); + Integer i2 = Integer.valueOf(4 / 2); NSRange range = new NSRange(1, 2); - NSArray array = new NSArray(new Object[] { o1, i2, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, i2, o3, o4 }); assertEquals(1, array.indexOfObject(i1, range)); assertFalse(1 == array.indexOfIdenticalObject(i1, range)); assertEquals(1, array.indexOfIdenticalObject(i2, range)); @@ -378,8 +416,9 @@ public class NSArrayTest extends TestCase { assertEquals(-1, array.indexOfIdenticalObject(i2, range2)); } + @Test public void testObjectAtIndex() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); assertEquals(o1, array.objectAtIndex(0)); assertEquals(o2, array.objectAtIndex(1)); assertEquals(o3, array.objectAtIndex(2)); @@ -391,9 +430,10 @@ public class NSArrayTest extends TestCase { } } + @Test public void testComponentsSeparatedByString() { String arrayString = "word0 word1 word2 word3"; - NSArray array = NSArray.componentsSeparatedByString(arrayString, " "); + NSArray<?> array = NSArray.componentsSeparatedByString(arrayString, " "); assertEquals("word0", array.objectAtIndex(0)); assertEquals("word1", array.objectAtIndex(1)); assertEquals("word2", array.objectAtIndex(2)); @@ -414,33 +454,37 @@ public class NSArrayTest extends TestCase { * } */ + @Test public void testToString() { - NSArray array = new NSArray(new Object[] { "o1", new Integer(1), "o3" }); + NSArray<?> array = new NSArray<Object>(new Object[] { "o1", Integer.valueOf(1), "o3" }); assertEquals("(o1, 1, o3)", array.toString()); } + @Test public void testContains() { - NSArray array = new NSArray(new Object[] { o1, o2, o4 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o4 }); assertTrue("Should contain object", array.contains(o1)); assertTrue("Should contain object", array.contains(o2)); assertFalse("Should not contain object", array.contains(o3)); assertTrue("Should contain object", array.contains(o4)); } + @Test public void testContainsAll() { - NSArray array = new NSArray(new Object[] { o1, o2, o4 }); - ArrayList list = new ArrayList(); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o4 }); + ArrayList<Object> list = new ArrayList<Object>(); list.add(o1); list.add(o2); assertTrue("Should have all elements of provided list.", array.containsAll(list)); - ArrayList list2 = new ArrayList(); + ArrayList<Object> list2 = new ArrayList<Object>(); list2.add(o2); list2.add(o3); assertFalse("Should not have all elements of provided list.", array.containsAll(list2)); } + @Test public void testGet() { - NSArray array = new NSArray(new Object[] { o1, o2, o4 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o4 }); assertEquals(o1, array.get(0)); assertEquals(o2, array.get(1)); assertEquals(o4, array.get(2)); @@ -450,43 +494,49 @@ public class NSArrayTest extends TestCase { } } + @Test public void testIndexOf() { - NSArray array = new NSArray(new Object[] { o1, o2, o4 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o4 }); assertEquals(0, array.indexOf(o1)); assertEquals(1, array.indexOf(o2)); assertEquals(-1, array.indexOf(o3)); assertEquals(2, array.indexOf(o4)); } + @Test public void testIsEmpty() { - assertFalse(new NSArray(new Object[] { o1, o2, o4 }).isEmpty()); - assertTrue(new NSArray(new Object[] {}).isEmpty()); + assertFalse(new NSArray<Object>(new Object[] { o1, o2, o4 }).isEmpty()); + assertTrue(new NSArray<Object>(new Object[] {}).isEmpty()); } + @Test public void testLastIndexOf() { - NSArray array = new NSArray(new Object[] { o1, o4, o2, o4 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o4, o2, o4 }); assertEquals(0, array.lastIndexOf(o1)); assertEquals(2, array.lastIndexOf(o2)); assertEquals(-1, array.lastIndexOf(o3)); assertEquals(3, array.lastIndexOf(o4)); } + @Test public void testSize() { - assertEquals(3, new NSArray(new Object[] { o1, o2, o4 }).size()); - assertEquals(1, new NSArray(new Object[] { o1 }).size()); - assertEquals(0, new NSArray(new Object[] {}).size()); + assertEquals(3, new NSArray<Object>(new Object[] { o1, o2, o4 }).size()); + assertEquals(1, new NSArray<Object>(new Object[] { o1 }).size()); + assertEquals(0, new NSArray<Object>(new Object[] {}).size()); } + @Test public void testToArray() { - NSArray array = new NSArray(new Object[] { o1, o2, o4 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o4 }); Object[] oarray = array.toArray(); assertEquals(oarray[0], o1); assertEquals(oarray[1], o2); assertEquals(oarray[2], o4); } + @Test public void testToArrayObjectArray() { - NSArray array = new NSArray(new Object[] { o1, o2, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o4 }); Object[] oa0 = new Object[3]; Object[] oa1 = array.toArray(oa0); assertSame(oa0, oa1); @@ -495,8 +545,9 @@ public class NSArrayTest extends TestCase { assertEquals(oa1[2], o4); } + @Test public void testAddIntObject() { - NSArray array = new NSArray(new Object[] { o1, o3, o4 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o3, o4 }); array.add(1, o2); assertEquals(0, array.indexOfObject(o1)); assertEquals(1, array.indexOfObject(o2)); @@ -504,8 +555,9 @@ public class NSArrayTest extends TestCase { assertEquals(3, array.indexOfObject(o4)); } + @Test public void testAddObject() { - NSArray array = new NSArray(new Object[] { o1, o2, o3 }); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2, o3 }); array.add(o4); assertEquals(0, array.indexOfObject(o1)); assertEquals(1, array.indexOfObject(o2)); @@ -513,9 +565,10 @@ public class NSArrayTest extends TestCase { assertEquals(3, array.indexOfObject(o4)); } + @Test public void testAddAllCollection() { - NSArray array = new NSArray(new Object[] { o1, o2 }); - ArrayList list = new ArrayList(); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o2 }); + ArrayList<Object> list = new ArrayList<Object>(); list.add(o3); list.add(o4); array.addAll(list); @@ -525,9 +578,10 @@ public class NSArrayTest extends TestCase { assertEquals(3, array.indexOfObject(o4)); } + @Test public void testAddAllIntCollection() { - NSArray array = new NSArray(new Object[] { o1, o4 }); - ArrayList list = new ArrayList(); + NSArray<Object> array = new NSArray<Object>(new Object[] { o1, o4 }); + ArrayList<Object> list = new ArrayList<Object>(); list.add(o2); list.add(o3); array.addAll(1, list); @@ -537,23 +591,26 @@ public class NSArrayTest extends TestCase { assertEquals(3, array.indexOfObject(o4)); } + @Test public void testClear() { - NSArray array = new NSArray(new Object[] { o1, o2 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2 }); array.clear(); assertEquals(0, array.size()); } + @Test public void testIterator() { - NSArray array = new NSArray(new Object[] { o1, o4 }); - Iterator i = array.iterator(); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o4 }); + Iterator<?> i = array.iterator(); assertEquals(o1, i.next()); assertEquals(o4, i.next()); assertFalse(i.hasNext()); } + @Test public void testListIterator() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); - ListIterator i = array.listIterator(); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); + ListIterator<?> i = array.listIterator(); assertEquals(o1, i.next()); assertEquals(o2, i.next()); assertEquals(o3, i.next()); @@ -565,9 +622,10 @@ public class NSArrayTest extends TestCase { assertFalse(i.hasNext()); } + @Test public void testListIteratorInt() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); - ListIterator i = array.listIterator(1); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); + ListIterator<?> i = array.listIterator(1); assertEquals(o2, i.next()); assertEquals(o3, i.next()); assertEquals(o4, i.next()); @@ -578,16 +636,18 @@ public class NSArrayTest extends TestCase { assertFalse(i.hasNext()); } + @Test public void testRemoveInt() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); assertEquals(o2, array.remove(1)); assertEquals(o3, array.remove(1)); assertEquals(o1, array.objectAtIndex(0)); assertEquals(o4, array.objectAtIndex(1)); } + @Test public void testRemoveObject() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); assertTrue(array.remove(o2)); assertTrue(array.remove(o3)); assertFalse(array.remove("blah")); @@ -595,9 +655,10 @@ public class NSArrayTest extends TestCase { assertEquals(o4, array.objectAtIndex(1)); } + @Test public void testRemoveAll() { - NSArray array = new NSArray(new Object[] { o1, o2, o3, o4 }); - ArrayList list = new ArrayList(); + NSArray<?> array = new NSArray<Object>(new Object[] { o1, o2, o3, o4 }); + ArrayList<Object> list = new ArrayList<Object>(); list.add(o1); list.add(o2); array.removeAll(list); diff --git a/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/NSXMLPropertyListTest.java b/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/NSXMLPropertyListTest.java new file mode 100644 index 0000000..0427f9e --- /dev/null +++ b/projects/net.wotonomy.foundation/src/test/java/net/wotonomy/foundation/NSXMLPropertyListTest.java @@ -0,0 +1,72 @@ +package net.wotonomy.foundation; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class NSXMLPropertyListTest { + + @Test + public void testStringParse() { + var res = NSXMLPropertyList.propertyListFromString("<plist><string>abc</string></plist>"); + if (res.isLeft()) { + NSPropertyList val = res.forceLeft(); + if (val.type != NSPropertyList.Type.STRING) { + fail("property list parsed incorrectly - got " + val.type.name() + " instead of string"); + } + NSPropertyList.String castVal = (NSPropertyList.String) val; + assertEquals("abc", castVal.getContents()); + } else { + NSError err = res.forceRight(); + fail("failed to parse property list - " + err.getDescription()); + } + } + + @Test + public void testArrayParse() { + var res = NSXMLPropertyList.propertyListFromString("<plist><array><string>abc</string><string>def</string></array></plist>"); + if (res.isLeft()) { + NSPropertyList val = res.forceLeft(); + if (val.type != NSPropertyList.Type.ARRAY) { + fail("property list parsed incorrectly - got " + val.type.name() + " instead of array"); + } + NSPropertyList.Array castVal = (NSPropertyList.Array) val; + NSArray<NSPropertyList> contents = castVal.getContents(); + + assertEquals(2, contents.size()); + assertEquals(NSPropertyList.Type.STRING, contents.get(0).type); + assertEquals(NSPropertyList.Type.STRING, contents.get(1).type); + assertEquals("abc", ((NSPropertyList.String)contents.get(0)).getContents()); + assertEquals("def", ((NSPropertyList.String)contents.get(1)).getContents()); + } else { + NSError err = res.forceRight(); + fail("failed to parse property list - " + err.getDescription()); + } + } + + @Test + public void testDictParse() { + var res = NSXMLPropertyList.propertyListFromString("<plist><dict><key>abc</key><integer>5</integer></dict></plist>"); + if (res.isLeft()) { + NSPropertyList val = res.forceLeft(); + if (val.type != NSPropertyList.Type.DICTIONARY) { + fail("property list parsed incorrectly - got " + val.type.name() + " instead of dictionary"); + } + NSPropertyList.Dictionary castVal = (NSPropertyList.Dictionary) val; + NSDictionary<String, NSPropertyList> contents = castVal.getContents(); + + assertEquals(1, contents.size()); + assertEquals(NSPropertyList.Type.INTEGER, contents.get("abc").type); + assertEquals(5, ((NSPropertyList.Integer)contents.get("abc")).getContents()); + } else { + NSError err = res.forceRight(); + StringBuilder underlyingErrors = new StringBuilder(); + for (NSError underlyingError : err.getUnderlyingErrors()) { + underlyingErrors.append(underlyingError.getDescription()); + underlyingErrors.append(", "); + } + + fail("failed to parse property list - " + err.getDescription() + "\n - " + underlyingErrors.toString()); + } + } +} diff --git a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/DebuggingDelegate.java b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/DebuggingDelegate.java index 3512c2a..a09df1e 100644 --- a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/DebuggingDelegate.java +++ b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/DebuggingDelegate.java @@ -101,7 +101,7 @@ public class DebuggingDelegate implements EODisplayGroup.Delegate { * @return An NSArray containing the objects to be displayed for the objects in * the specified list. */ - public NSArray displayGroupDisplayArrayForObjects(EODisplayGroup aDisplayGroup, List aList) { + public <T> NSArray<T> displayGroupDisplayArrayForObjects(EODisplayGroup aDisplayGroup, List<T> aList) { return get("displayGroupDisplayArrayForObjects", aDisplayGroup, aList); } @@ -155,7 +155,7 @@ public class DebuggingDelegate implements EODisplayGroup.Delegate { * insertion. */ public boolean displayGroupShouldInsertObject(EODisplayGroup aDisplayGroup, Object anObject, int anIndex) { - return ask("displayGroupShouldInsertObject", aDisplayGroup, new Object[] { anObject, new Integer(anIndex) }); + return ask("displayGroupShouldInsertObject", aDisplayGroup, new Object[] { anObject, Integer.valueOf(anIndex) }); } /** @@ -190,9 +190,9 @@ public class DebuggingDelegate implements EODisplayGroup.Delegate { * This method is called by displayGroupDisplayArrayForObjects. This * implementation calls report and returns a copy of the specified list. */ - protected NSArray get(String aTitle, EODisplayGroup aDisplayGroup, List anObjectList) { + protected <T> NSArray<T> get(String aTitle, EODisplayGroup aDisplayGroup, List<T> anObjectList) { report(aTitle, aDisplayGroup, new Object[] { anObjectList }); - return new NSArray(anObjectList); + return new NSArray<>(anObjectList); } /** diff --git a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/DelegateAdapter.java b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/DelegateAdapter.java index e801406..c7e549c 100644 --- a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/DelegateAdapter.java +++ b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/DelegateAdapter.java @@ -101,8 +101,8 @@ public class DelegateAdapter implements EODisplayGroup.Delegate { * @return An NSArray containing the objects to be displayed for the objects in * the specified list. */ - public NSArray displayGroupDisplayArrayForObjects(EODisplayGroup aDisplayGroup, List aList) { - return new NSArray(aList); + public <T> NSArray<T> displayGroupDisplayArrayForObjects(EODisplayGroup aDisplayGroup, List<T> aList) { + return new NSArray<>(aList); } /** diff --git a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java index 033f0a9..e2b2b11 100644 --- a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java +++ b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/EODisplayGroup.java @@ -1653,7 +1653,7 @@ public class EODisplayGroup extends Observable implements EOObserving, EOEditing * @return An NSArray containing the objects to be displayed for the objects in * the specified list. */ - NSArray displayGroupDisplayArrayForObjects(EODisplayGroup aDisplayGroup, List aList); + <T> NSArray<T> displayGroupDisplayArrayForObjects(EODisplayGroup aDisplayGroup, List<T> aList); /** * Called by the specified display group before it attempts to change the diff --git a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/ObservableArray.java b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/ObservableArray.java index e18ed3b..7755e8f 100644 --- a/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/ObservableArray.java +++ b/projects/net.wotonomy.ui/src/main/java/net/wotonomy/ui/ObservableArray.java @@ -36,7 +36,9 @@ import net.wotonomy.foundation.NSRange; * probably call each other. However, EOObserverCenter will only register us * once per object. */ -class ObservableArray extends NSMutableArray { +class ObservableArray<T> extends NSMutableArray<T> { + private static final long serialVersionUID = 9098414040194393263L; + EOObserving observer; ObservableArray(EOObserving anObserver) { @@ -60,7 +62,7 @@ class ObservableArray extends NSMutableArray { /** * Adds all objects in the specified collection. */ - public void addObjectsFromArray(Collection aCollection) { + public void addObjectsFromArray(Collection<T> aCollection) { addAll(aCollection); } @@ -112,7 +114,7 @@ class ObservableArray extends NSMutableArray { /** * Removes all objects in the specified collection from the array. */ - public void removeObjectsInArray(Collection aCollection) { + public void removeObjectsInArray(Collection<T> aCollection) { removeAll(aCollection); } @@ -134,7 +136,7 @@ class ObservableArray extends NSMutableArray { * objects are removed. If otherRange is larger than currentRange, the extra * objects are added. */ - public void replaceObjectsInRange(NSRange currentRange, List otherArray, NSRange otherRange) { + public void replaceObjectsInRange(NSRange currentRange, List<T> otherArray, NSRange otherRange) { if ((currentRange == null) || (otherArray == null) || (otherRange == null)) return; @@ -183,7 +185,7 @@ class ObservableArray extends NSMutableArray { /** * Removes all occurences of the specified object, comparing by reference. */ - public void removeIdenticalObject(Object anObject) { + public void removeIdenticalObject(T anObject) { EOObserverCenter.removeObserver(observer, anObject); super.removeIdenticalObject(anObject); } @@ -191,32 +193,32 @@ class ObservableArray extends NSMutableArray { /** * Inserts the specified object into this array at the specified index. */ - public void insertObjectAtIndex(Object anObject, int anIndex) { + public void insertObjectAtIndex(T anObject, int anIndex) { add(anIndex, anObject); } /** * Replaces the object at the specified index with the specified object. */ - public void replaceObjectAtIndex(int anIndex, Object anObject) { + public void replaceObjectAtIndex(int anIndex, T anObject) { set(anIndex, anObject); } /** * Adds the specified object to the end of this array. */ - public void addObject(Object anObject) { + public void addObject(T anObject) { add(anObject); } // interface List: mutators - public void add(int index, Object element) { + public void add(int index, T element) { EOObserverCenter.addObserver(observer, element); super.add(index, element); } - public boolean add(Object o) { + public boolean add(T o) { EOObserverCenter.addObserver(observer, o); return super.add(o); } @@ -245,7 +247,7 @@ class ObservableArray extends NSMutableArray { super.clear(); } - public Object remove(int index) { + public T remove(int index) { EOObserverCenter.removeObserver(observer, get(index)); return super.remove(index); } @@ -267,7 +269,7 @@ class ObservableArray extends NSMutableArray { throw new UnsupportedOperationException(); } - public Object set(int index, Object element) { + public T set(int index, T element) { EOObserverCenter.removeObserver(observer, get(index)); EOObserverCenter.addObserver(observer, element); return super.set(index, element); |
