All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dellroad.stuff.jibx.ParseUtil Maven / Gradle / Ivy

There is a newer version: 3.0.8
Show newest version

/*
 * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
 */

package org.dellroad.stuff.jibx;

import java.net.Inet4Address;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.dellroad.stuff.java.IdGenerator;
import org.dellroad.stuff.net.IPv4Util;
import org.dellroad.stuff.string.ByteArrayEncoder;
import org.dellroad.stuff.string.DateEncoder;
import org.dellroad.stuff.string.StringEncoder;
import org.jibx.runtime.JiBXParseException;

/**
 * JiBX parsing utility methods. These methods can be used as JiBX value serializer/deserializer methods.
 */
public final class ParseUtil {

    private static final String[] BOOLEAN_TRUES = { "1", "true", "yes" };
    private static final String[] BOOLEAN_FALSES = { "0", "false", "no" };
    private static final String XSD_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";

    private static final String TIME_INTERVAL_PATTERN
      = "(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?((([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))s)?";

    private ParseUtil() {
    }

    /**
     * Deserialize a {@link String}, allowing arbitrary characters via backslash escapes.
     * The string will be decoded by {@link StringEncoder#decode}.
     *
     * @param string XML encoding created by {@link #serializeString}
     * @return decoded value
     * @throws JiBXParseException if the parse fails
     * @see StringEncoder#decode
     */
    public static String deserializeString(String string) throws JiBXParseException {
        try {
            return StringEncoder.decode(string);
        } catch (IllegalArgumentException e) {
            throw new JiBXParseException("invalid encoded string", string, e);
        }
    }

    /**
     * Serialize a {@link String}.
     * The string will be encoded by {@link StringEncoder#encode}.
     *
     * @param string value to encode for XML
     * @return encoded value
     * @see StringEncoder#encode
     */
    public static String serializeString(String string) {
        return StringEncoder.encode(string, false);
    }

    /**
     * Deserialize a {@link TimeZone}.
     *
     * @param string XML encoding created by {@link #serializeTimeZone}
     * @return decoded value
     * @throws JiBXParseException if the parse fails
     * @see #serializeTimeZone
     */
    public static TimeZone deserializeTimeZone(String string) throws JiBXParseException {
        if (!new HashSet(Arrays.asList(TimeZone.getAvailableIDs())).contains(string))
            throw new JiBXParseException("unrecognized time zone", string);
        return TimeZone.getTimeZone(string);
    }

    /**
     * Serialize a {@link TimeZone}.
     *
     * @param timeZone value to encode for XML
     * @return encoded value
     * @see #deserializeTimeZone
     */
    public static String serializeTimeZone(TimeZone timeZone) {
        return timeZone.getID();
    }

    /**
     * Deserialize an {@link URI}.
     *
     * 

* Note: there is no need for a custom serializer, as {@link URI#toString} already does the right thing. * * @param string XML encoding created by {@link URI#toString} * @return decoded value * @throws JiBXParseException if the parse fails */ public static URI deserializeURI(String string) throws JiBXParseException { try { return new URI(string); } catch (URISyntaxException e) { throw new JiBXParseException("invalid URI", string, e); } } /** * Deserialize an object by reference. * *

* Invoke this method from your own custom deserializer to produce an result of the correct type. * *

* The object must have been unmarshalled already and had its ID registered via {@link IdMapper#setId}. * * @param expected type * @param string XML encoding created by {@link #serializeReference} * @param type expected type * @return decoded value * @throws IllegalArgumentException if {@code string} cannot be parsed * @throws JiBXParseException if the reference has not been registered to any object yet (e.g., forward reference) * @throws JiBXParseException if the referenced object is not an instance of {@code type} * @see #serializeReference * @see IdMapper */ public static T deserializeReference(String string, Class type) throws JiBXParseException { if (string == null) return null; long id; try { id = IdMapper.parseId(string); } catch (IllegalArgumentException e) { throw new JiBXParseException("invalid object reference", string, e); } Object obj = IdGenerator.get().getObject(id); if (obj == null) throw new JiBXParseException("unregistered object reference `" + string + "'; possible forward reference", string); try { return type.cast(obj); } catch (ClassCastException e) { throw new JiBXParseException("object reference `" + string + "' is assigned to an instance of " + obj.getClass() + " which is not assignable to " + type, string); } } /** * Serialize an object by reference. * *

* The object must have been marshalled already and had its ID assigned via {@link IdMapper#getId}. * * @param obj object to encode by reference for XML * @return encoded reference * @throws IllegalArgumentException if the object has not been registered yet (e.g., forward reference) * @see #deserializeReference * @see IdMapper */ public static String serializeReference(Object obj) { if (obj == null) return null; long id = IdGenerator.get().checkId(obj); if (id == 0) throw new IllegalArgumentException("unregistered object; possible forward reference to " + obj); return IdMapper.formatId(id); } /** * Deserialize a {@link UUID}. * *

* Note: there is no need for a custom serializer, as {@link UUID#toString} already does the right thing. * * @param string XML encoding created by {@link UUID#toString} * @return decoded value * @throws JiBXParseException if the parse fails */ public static UUID deserializeUUID(String string) throws JiBXParseException { try { return UUID.fromString(string); } catch (IllegalArgumentException e) { throw new JiBXParseException("invalid UUID", string, e); } } /** * Derialize a millisecond-granularity time interval, e.g., "30s", "1d12h", "0.250s", etc. * * @param string XML encoding created by {@link #serializeTimeInterval} * @return decoded value * @throws JiBXParseException if the parse fails * @see #serializeTimeInterval */ public static long deserializeTimeInterval(String string) throws JiBXParseException { // Apply regular expression Matcher m = Pattern.compile(TIME_INTERVAL_PATTERN).matcher(string); if (string.length() == 0 || !m.matches()) throw new JiBXParseException("invalid time interval", string); // Parse regex groups Object[][] groups = new Object[][] { { 2, 24 * 60 * 60 * 1000 }, { 4, 60 * 60 * 1000 }, { 6, 60 * 1000 }, { 8, 1000 }, }; long value = 0; for (int i = 0; i < groups.length; i++) { String group = m.group((Integer)groups[i][0]); if (group == null || group.length() == 0) continue; double num; try { num = Double.parseDouble(group); } catch (NumberFormatException e) { throw new JiBXParseException("invalid time interval", string); } value += Math.round(num * (Integer)groups[i][1]); if (value < 0) throw new JiBXParseException("time interval is too large", string); } // Done return value; } /** * Serialize a millisecond-granularity time interval, e.g., "30s", "1d12h", "0.250s", etc. * * @param value millisecond value to encode * @return encoded value * @see #deserializeTimeInterval */ public static String serializeTimeInterval(long value) { if (value < 0) throw new IllegalArgumentException("negative value"); StringBuilder b = new StringBuilder(32); long days = value / (24 * 60 * 60 * 1000); value = value % (24 * 60 * 60 * 1000); if (days > 0) b.append(days).append('d'); long hours = value / (60 * 60 * 1000); value = value % (60 * 60 * 1000); if (hours > 0) b.append(hours).append('h'); long minutes = value / (60 * 1000); value = value % (60 * 1000); if (minutes > 0) b.append(minutes).append('m'); long millis = value; if (millis != 0 || b.length() == 0) { if (millis >= 1000 && millis % 1000 == 0) b.append(String.format("%ds", millis / 1000)); else b.append(String.format("%.3fs", millis / 1000.0)); } return b.toString(); } /** * Deserialize a byte array using {@link ByteArrayEncoder}. * * @param string XML encoding created by {@link #serializeByteArray} * @return decoded value * @throws JiBXParseException if the parse fails * @see ByteArrayEncoder */ public static byte[] deserializeByteArray(String string) throws JiBXParseException { try { return ByteArrayEncoder.decode(string); } catch (IllegalArgumentException e) { throw new JiBXParseException("invalid byte array", string, e); } } /** * Serialize a byte array using {@link ByteArrayEncoder}. * * @param array array to encode * @return encoded array * @see ByteArrayEncoder */ public static String serializeByteArray(byte[] array) { return ByteArrayEncoder.encode(array); } /** * Deserialize a byte array using MAC address notation (colon-separated). Each byte must have two digits. * * @param string XML encoding created by {@link #serializeByteArrayWithColons} * @return decoded value * @throws JiBXParseException if the parse fails * @see #serializeByteArrayWithColons */ public static byte[] deserializeByteArrayWithColons(String string) throws JiBXParseException { if (string.length() == 0) return new byte[0]; if (string.length() % 3 != 2) throw new JiBXParseException("invalid byte array", string); char[] nocolons = new char[((string.length() + 1) / 3) * 2]; int j = 0; for (int i = 0; i < string.length(); i++) { if (i % 3 == 2) { if (string.charAt(i) != ':') throw new JiBXParseException("invalid byte array", string); continue; } nocolons[j++] = string.charAt(i); } try { return ByteArrayEncoder.decode(new String(nocolons)); } catch (IllegalArgumentException e) { throw new JiBXParseException("invalid byte array", string, e); } } /** * Serialize a byte array using MAC address notation (colon-separated). Each byte will have two digits. * * @param array array to encode * @return encoded array * @see #deserializeByteArrayWithColons */ public static String serializeByteArrayWithColons(byte[] array) { char[] colons = new char[array.length * 3 - 1]; String nocolons = ByteArrayEncoder.encode(array); int j = 0; for (int i = 0; i < nocolons.length(); i += 2) { colons[j++] = nocolons.charAt(i); colons[j++] = nocolons.charAt(i + 1); if (j < colons.length) colons[j++] = ':'; } return new String(colons); } /** * Deserialize an {@link Inet4Address}. No DNS name resolution of any kind is performed. * * @param string XML encoding created by {@link #serializeInet4Address} * @return decoded value * @throws JiBXParseException if the parse fails * @see #serializeInet4Address */ public static Inet4Address deserializeInet4Address(String string) throws JiBXParseException { try { return IPv4Util.fromString(string); } catch (IllegalArgumentException e) { throw new JiBXParseException("invalid IPv4 address", string); } } /** * Serialize an {@link Inet4Address}. * * @param addr address to encode * @return encoded address * @see #deserializeInet4Address */ public static String serializeInet4Address(Inet4Address addr) { return IPv4Util.toString(addr); } /** * Deserialize a {@link SimpleDateFormat}. * * @param string XML encoding created by {@link #serializeSimpleDateFormat} * @return decoded value * @throws JiBXParseException if the parse fails * @see #serializeSimpleDateFormat */ public static SimpleDateFormat deserializeSimpleDateFormat(String string) throws JiBXParseException { try { return new SimpleDateFormat(string); } catch (IllegalArgumentException e) { throw new JiBXParseException("invalid date format", string, e); } } /** * Serialize a {@link SimpleDateFormat}. * * @param format date format to encode * @return encoded format * @see #deserializeSimpleDateFormat */ public static String serializeSimpleDateFormat(SimpleDateFormat format) { return format.toPattern(); } /** * Deserialize a {@link Date}. This method can be used as a deserialize support method. * * @param string XML encoding created by {@link #serializeDate(Date, String)} * @param format format for {@link SimpleDateFormat} * @return decoded value * @throws JiBXParseException if the parse fails * @see #serializeDate */ public static Date deserializeDate(String string, String format) throws JiBXParseException { try { return deserializeSimpleDateFormat(format).parse(string); } catch (java.text.ParseException e) { throw new JiBXParseException("invalid date", string, e); } } /** * Serialize a {@link Date}. This method can be used as a serialize support method. * * @param date date to serialize * @param format format for {@link SimpleDateFormat}. * @return encoded date * @see #deserializeDate * @throws JiBXParseException if {@code format} is invalid */ public static String serializeDate(Date date, String format) throws JiBXParseException { return deserializeSimpleDateFormat(format).format(date); } /** * Deserialize a {@link Date} in the format supported by {@link DateEncoder}. * * @param date XML encoding created by {@link #serializeDate(Date)} * @return decoded value * @throws JiBXParseException if the parse fails * @see DateEncoder */ public static Date deserializeDate(String date) throws JiBXParseException { try { return DateEncoder.decode(date); } catch (IllegalArgumentException e) { throw new JiBXParseException("invalid date", date, e); } } /** * Serialize a {@link Date} in the format supported by {@link DateEncoder}. * * @param date date to encode * @return encoded date * @see DateEncoder */ public static String serializeDate(Date date) { return DateEncoder.encode(date); } /** * Deserialize a {@link Date} in XSD dateTime format. * * @param date XML encoding created by {@link #serializeXSDDateTime} * @return decoded value * @throws JiBXParseException if the parse fails * @see #serializeXSDDateTime * @see XSD dateTime datatype */ public static Date deserializeXSDDateTime(String date) throws JiBXParseException { try { return deserializeSimpleDateFormat(XSD_DATE_FORMAT).parse(date); } catch (java.text.ParseException e) { throw new JiBXParseException("invalid date", date, e); } } /** * Serialize a {@link Date} to XSD dateTime format. * * @param date date to encode * @return encoded date * @see #deserializeXSDDateTime * @see XSD dateTime datatype */ public static String serializeXSDDateTime(Date date) { return new SimpleDateFormat(XSD_DATE_FORMAT).format(date); } /** * Deserialize a {@link Pattern}. * *

* Note: there is no need for a custom serializer, as {@link Pattern#toString} already does the right thing. * * @param string XML encoding created by {@link Pattern#toString} * @return decoded value * @throws JiBXParseException if the parse fails */ public static Pattern deserializePattern(String string) throws JiBXParseException { try { return Pattern.compile(string); } catch (PatternSyntaxException e) { throw new JiBXParseException("invalid regular expression", string, e); } } /** * JiBX {@link String} deserializer that normalizes a string as is required by the {@code xsd:token} XSD type. * This removes leading and trailing whitespace, and collapses all interior whitespace * down to a single space character. * * @param string string to normalize * @return normalized string * @throws NullPointerException if {@code string} is null */ public static String normalize(String string) { return string.trim().replaceAll("\\s+", " "); } /** * JiBX {@link String} deserializer support method that verifies that the input string matches the * given regular expression. This method can be invoked by custom deserializers that supply the * regular expression to it. * * @param regex regular expression pattern which input must match * @param string input string * @return decoded value * @throws NullPointerException if {@code string} of {@code regex} is null * @throws JiBXParseException if {@code string} does not match {@code regex} * @throws java.util.regex.PatternSyntaxException * if {@code regex} is not a valid regular expression */ public static String deserializeMatching(String regex, String string) throws JiBXParseException { if (!string.matches(regex)) throw new JiBXParseException("input does not match pattern \"" + regex + "\"", string); return string; } /** * Boolean parser that allows "yes" and "no" as well as the usual "true", "false", "0", "1". * Comparisons are case-insensitive. * * @param string XML encoding of boolean value * @return decoded value * @throws JiBXParseException if the value is not recognizable as a boolean * @see #deserializeBooleanStrictly */ public static boolean deserializeBoolean(String string) throws JiBXParseException { for (String s : BOOLEAN_TRUES) { if (string.equalsIgnoreCase(s)) return true; } for (String s : BOOLEAN_FALSES) { if (string.equalsIgnoreCase(s)) return false; } throw new JiBXParseException("invalid Boolean value", string); } /** * Deserialize a boolean strictly, only allowing the values {@code true} or {@code false}. * * @param string XML encoding created by {@link Boolean#toString} * @return decoded value * @throws JiBXParseException if the parse fails * @see #deserializeBoolean */ public static boolean deserializeBooleanStrictly(String string) throws JiBXParseException { if ("true".equals(string)) return true; if ("false".equals(string)) return false; throw new JiBXParseException("invalid boolean value; must be `true' or `false'", string); } /** * Deserialize an array of integers separated by commas and/or whitespace. * * @param string XML encoding created by {@link #serializeIntArray} * @return decoded integer array * @throws JiBXParseException if the parse fails * @see #serializeIntArray */ public static int[] deserializeIntArray(String string) throws JiBXParseException { String[] ints = string.trim().split("(\\s*,\\s*|\\s+)"); if (ints.length == 0 || ints[0].length() == 0) return new int[0]; int[] array = new int[ints.length]; try { for (int i = 0; i < array.length; i++) array[i] = Integer.parseInt(ints[i]); } catch (NumberFormatException e) { throw new JiBXParseException("invalid integer list", string, e); } return array; } /** * Serialize an array of integers. Example: "1, 2, 3". * * @param array integer array * @return string encoding of {@code array} * @see #deserializeIntArray */ public static String serializeIntArray(int[] array) { StringBuilder buf = new StringBuilder(array.length * 2); for (int i = 0; i < array.length; i++) { if (i > 0) buf.append(", "); buf.append(array[i]); } return buf.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy