/** * Parse a value. * * @param tb Source. * @param type The type of the array, may be any.. * @param valueList Result added here. * @return Return true if a value has been added to the valueList and the cursor advanced or false * and the cursor is as it was. */ private static boolean value(Text t, DataType type, List<Object> valueList) { boolean result; if (t.isEof()) { result = false; } else if (t.consume("null")) { valueList.add(null); result = true; } else { switch (type) { case z: case Z: case f: case F: result = number(t, type, valueList); break; case text: result = text(t, valueList); break; case identifier: result = identifier(t, valueList); break; case path: result = path(t, valueList); break; case datetime: result = datetime(t, valueList); break; case date: result = date(t, valueList); break; case time: result = time(t, valueList); break; case bool: result = bool(t, valueList); break; case cardinality: result = cardinality(t, valueList); break; case any: result = any(t, valueList); break; default: throw new UnexpectedException("value: " + type); } } return result; }
/** * Parse a string value and covert it to its proper data type. * * @param value * @return The parsed value. * @throws ParsingException Thrown if not Advisory is available and there was an error parsing. */ @SuppressWarnings("unchecked") public static <T> T parseValue(String value) throws ParsingException { final T result; assert value != null; /* * todo Restructure the whole class so I've got parse and parseArray as * static methods and the share code properly. */ if ("null".equals(value)) { result = null; } else { final List<Object> list = new ArrayList<>(); final Text t = new Text(); t.append(value); if (any(t, list) && t.isEof()) { result = (T) list.get(0); } else { error("Count not parse value: " + value); result = null; } } return result; }
private static boolean number(Text t, DataType requiredType, List<Object> valueList) { // posNumber // : cardinality // | '0b' BinaryDigits+ [zZ]? // | '0x' HexDigit+ [zZ]? // | '-'? Pint? ( '.' Digit+ ) ('e' Pint) [fF] // | '-'? Pint [zZfF]? // ; final boolean result; assert !t.isEof() : "Should have been checked by caller"; if (binary(t, valueList, requiredType) || hex(t, valueList, requiredType)) { // ?todo I could allow negatives here // ( binary | hex | '0' ) result = true; } else { // ( Int DecPart | Int | DecPart ) ( 'e' Int )? [zZfF]? final int start = t.cursor(); if ((t.consumeInt() && (decPart(t) || true) && (exponent(t) || true)) || (t.consume('-') || true) && decPart(t) && (exponent(t) || true)) { final String string = t.getString(start); final Object value = deriveType(string, requiredType, t, 10); valueList.add(value); result = true; } else { result = false; } } return result; }
/** * Given the parsing context and what numerical data type is expected convert a string to the * correct type. Note no attempt is made to let the magnitude of the number influence our choice. * * @param string The string to convert to a number. E.g. "123.3e2". If it contains a '.' or an 'e' * then the type must either be f or F. * @param requiredType Either z, Z, f, F or any. * @param tb The source. The cursor will be at the end of the number but any type specifier will * not have been consumed. If there is one then we'll eat it. * @return The derived type. * @throws ParsingException If there is a clash of types. */ private static Object deriveType(String string, DataType requiredType, Text t, int radix) { final Object result; // Figure out the correct type... final DataType derivedType; if (t.isEof()) { if (requiredType == DataType.any) { if (string.indexOf('.') >= 0 || string.indexOf('e') >= 0) { derivedType = DataType.f; } else { derivedType = DataType.z; } } else { derivedType = requiredType; } } else { final char c = t.peek(); if (c == 'z' || c == 'Z' || c == 'f' || c == 'F') { t.consume(c); derivedType = DataType.valueOf(String.valueOf(c)); if (!(requiredType == DataType.any || requiredType == derivedType)) { throw new ParsingException("Incompatible type: " + string + c); } } else { if (requiredType == DataType.any) { if (string.indexOf('.') >= 0 || string.indexOf('e') >= 0) { derivedType = DataType.f; } else { derivedType = DataType.z; } } else { derivedType = requiredType; } } } switch (derivedType) { case z: result = new Long(Long.parseLong(string, radix)); break; case Z: result = new BigInteger(string, radix); break; case f: result = new Double(string); break; case F: result = new BigDecimal(string); break; // $CASES-OMITTED$ default: throw new UnexpectedException("toType: " + derivedType); } return result; }
/** * Parse a string into a list of data values. White space is supported only in between the [square * brackets]. * * @param string A string representation of an Oak property array, e.g. [ true, null, false ] * @param type The type of the array, may be 'any'. * @return A potentially empty but not null list of values. */ @SuppressWarnings("unchecked") public static <T> List<T> parseArray(String string, DataType type) { final List<?> result; final Text t = new Text(); t.append(string); if (t.consume('[') && (result = elementList(t, type)) != null && (t.ws() && t.consume(']')) && t.isEof()) { // OK } else { throw new ParsingException("Invalid array: " + string); } return (List<T>) result; }