/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.isis.core.runtime.services.memento;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.joda.time.LocalDate;
import org.apache.isis.applib.services.bookmark.Bookmark;
import org.apache.isis.core.commons.exceptions.IsisException;
class Dom4jUtil {
private Dom4jUtil(){}
private final static String NULL_MARKER = "$$_isis_null_value_$$";
static void addChild(final Element el, final String name, final Object value) {
el.addElement(name).setText(encodeForNulls(value));
}
static boolean isSupportedClass(final Class<?> cls) {
return Parseable.isSupported(cls);
}
/**
* @param el
* @param name
* @param cls - see {@link Parseable}
* @return
*/
static <T> T getChild(final Element el, final String name, final Class<T> cls) {
Parseable.assertSupported(cls);
final Element child = el.element(name);
if(child == null) {
return null;
}
final String str = decodeForNulls(child.getText());
if(str == null) {
return null;
}
return Parseable.parse(str, cls);
}
static Document parse(final String xmlStr) {
try {
final SAXReader saxReader = new SAXReader();
Document doc = saxReader.read(new StringReader(xmlStr));
return doc;
} catch (DocumentException e) {
throw new IsisException(e);
}
}
static String asString(final Document doc) {
XMLWriter writer = null;
final StringWriter sw = new StringWriter();
try {
// previously this code used pretty print.
// however, that tripped up on strings with double spaces in them; the double space was normalized
// to a single space!
// OutputFormat outputFormat = OutputFormat.createPrettyPrint();
// writer = new XMLWriter(sw, outputFormat);
writer = new XMLWriter(sw);
writer.write(doc);
return sw.toString();
} catch (IOException e) {
throw new IsisException(e);
} finally {
if(writer != null) {
try {
writer.close();
} catch (IOException e) {
// ignore
}
}
}
}
private static String encodeForNulls(final Object value) {
return value != null ? Parseable.format(value) : NULL_MARKER;
}
private static String decodeForNulls(final String valueStr) {
return NULL_MARKER.equals(valueStr)? null: valueStr;
}
// //////////////////////////////////////
static enum Parseable {
STRING(String.class) {
@SuppressWarnings("unchecked")
public <T> T parseStr(String str, Class<T> cls) {
return (T) str;
}
},
BOOLEAN(Boolean.class, boolean.class) {
@SuppressWarnings("unchecked")
@Override
public <T> T parseStr(String str, Class<T> cls) {
return (T) new Boolean(str);
}
},
BYTE(Byte.class, byte.class) {
@SuppressWarnings("unchecked")
@Override
public <T> T parseStr(String str, Class<T> cls) {
return (T) new Byte(str);
}
},
SHORT(Short.class, short.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new Short(str);
}
},
INTEGER(Integer.class, int.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new Integer(str);
}
},
LONG(Long.class, long.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new Long(str);
}
},
FLOAT(Float.class, float.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new Float(str);
}
},
DOUBLE(Double.class, double.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new Double(str);
}
},
BIG_DECIMAL(BigDecimal.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new BigDecimal(str);
}
},
BIG_INTEGER(BigInteger.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new BigInteger(str);
}
},
LOCAL_DATE(LocalDate.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new LocalDate(str);
}
},
JAVA_UTIL_DATE(Date.class) {
@SuppressWarnings({ "unchecked" })
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new Date(Long.parseLong(str));
}
@Override
<T> String format(Object o, Class<T> cls) {
return Long.toString(((Date) o).getTime());
}
},
ENUM(Enum.class) {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
<T> T parseStr(String str, Class<T> cls) {
Class rawCls = cls;
return (T) valueOf(str, rawCls);
}
private <E extends Enum<E>> E valueOf(String name, Class<E> cls) {
return Enum.valueOf(cls, name);
}
},
BOOKMARK(Bookmark.class) {
@SuppressWarnings("unchecked")
@Override
<T> T parseStr(String str, Class<T> cls) {
return (T) new Bookmark(str);
}
};
private final Class<?>[] classes;
private Parseable(Class<?>... classes) {
this.classes = classes;
}
public Class<?>[] getClasses() {
return classes;
}
abstract <T> T parseStr(String str, Class<T> cls);
<T> String format(Object o, Class<T> cls) {
return o.toString();
}
// //////////////////////////////////////
static <T> T parse(final String str, final Class<? extends T> cls) {
assertSupported(cls);
for (Parseable sc : values()) {
for (Class<?> eachCls: sc.getClasses()) {
if(eachCls.isAssignableFrom(cls)) {
if(!eachCls.isPrimitive() || str != null) {
return (T) sc.parseStr(str, cls);
}
}
}
}
return null;
}
static String format(final Object o) {
if(isSupported(o.getClass())) {
for (Parseable sc : values()) {
for (Class<?> eachCls: sc.getClasses()) {
if(eachCls.isAssignableFrom(o.getClass())) {
return sc.format(o, o.getClass());
}
}
}
}
return o.toString();
}
static boolean isSupported(final Class<?> cls) {
for (Parseable sc : values()) {
for (Class<?> eachCls: sc.getClasses()) {
if(eachCls.isAssignableFrom(cls)) {
return true;
}
}
}
return false;
}
static void assertSupported(final Class<?> cls) {
if(!isSupported(cls)) {
throw new IllegalArgumentException("Parsing string to type " + cls.getName() + " is not supported");
}
}
}
}