/*
* Encog(tm) Core v2.5 - Java Version
* http://www.heatonresearch.com/encog/
* http://code.google.com/p/encog-java/
* Copyright 2008-2010 Heaton Research, Inc.
*
* Licensed 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.
*
* For more information on Heaton Research copyrights, licenses
* and trademarks visit:
* http://www.heatonresearch.com/copyright
*/
package org.encog.persist.persistors.generic;
import java.io.File;
import java.lang.reflect.Field;
import java.util.Collection;
import org.encog.EncogError;
import org.encog.parse.tags.Tag.Type;
import org.encog.parse.tags.read.ReadXML;
import org.encog.persist.EncogPersistedObject;
import org.encog.persist.PersistError;
import org.encog.util.obj.ReflectionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A generic class used to take an XML segment and produce an object for it.
* Some of the Encog persistors make use of this class. The Encog generic
* persistor makes use of this class.
*
* @author jheaton
*
*/
public class XML2Object {
/**
* The object mapper to use to resolve references.
*/
private final ObjectMapper mapper = new ObjectMapper();
/**
* Used to read the XML.
*/
private ReadXML in;
/**
* The logging object.
*/
@SuppressWarnings("unused")
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* Load an object from XML.
*
* @param in
* The XML reader.
* @param target
* The object to load.
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
public void load(final ReadXML in, final EncogPersistedObject target) {
this.in = in;
this.mapper.clear();
target.setName(in.getTag().getAttributeValue("name"));
target.setDescription(in.getTag().getAttributeValue("description"));
loadActualObject(null, target);
this.mapper.resolve();
}
/**
* Load an object.
*
* @param objectField
* The object's field.
* @param target The object that will get the value.
*/
@SuppressWarnings("unchecked")
private void loadActualObject(final Field objectField, final Object target) {
try {
// handle attributes
for (final String key : this.in.getTag().getAttributes().keySet()) {
if (key.equals("native")) {
continue;
}
// see if there is an id
if (key.equals(Object2XML.REFF_ID)) {
final int ref = Integer.parseInt(this.in.getTag()
.getAttributeValue(Object2XML.REFF_ID));
this.mapper.addObjectMapping(ref, target);
continue;
}
final Field field = ReflectionUtil.findField(target.getClass(),
key);
if( field!=null ) {
final String value = this.in.getTag().getAttributeValue(key);
setFieldValue(field, target, value);
}
}
// handle properties
while (this.in.readToTag()) {
if (this.in.getTag().getType() == Type.BEGIN) {
final String tagName = this.in.getTag().getName();
final Field field = ReflectionUtil.findField(target
.getClass(), tagName);
if (field == null) {
continue;
}
field.setAccessible(true);
final Object currentValue = field.get(target);
Class<?> type = field.getType();
if( type.isEnum() ) {
final String value = this.in.readTextToTag();
setFieldValue(field, target, value);
}
else if( (type == String.class) || type.isPrimitive() ) {
final String value = this.in.readTextToTag();
setFieldValue(field, target, value);
} else if (currentValue instanceof Collection) {
loadCollection((Collection<Object>) currentValue);
} else if (field.getType() == File.class) {
final String value = this.in.readTextToTag();
final File file = new File(value);
field.set(target, file);
} else {
this.in.readToTag();
if( this.in.getTag().getType()!=Type.END) {
final Object nextObject = loadObject(field, target);
field.set(target, nextObject);
}
}
} else if (this.in.getTag().getType() == Type.END) {
if (this.in.getTag().getName().equals(
target.getClass().getSimpleName())) {
return;
}
}
}
} catch (final IllegalArgumentException e) {
throw new EncogError(e);
} catch (final IllegalAccessException e) {
throw new EncogError(e);
} catch (final InstantiationException e) {
throw new EncogError(e);
}
}
/**
* Load a collection.
* @param collection The collection to load.
*/
private void loadCollection(final Collection<Object> collection) {
try {
while (this.in.readToTag()) {
if (this.in.getTag().getType() == Type.BEGIN) {
final String tagName = this.in.getTag().getName();
final Class< ? > c = ReflectionUtil
.resolveEncogClass(tagName);
final Object target = c.newInstance();
loadActualObject(null, target);
collection.add(target);
} else if (this.in.getTag().getType() == Type.END) {
return;
}
}
} catch (final InstantiationException e) {
throw new EncogError(e);
} catch (final IllegalAccessException e) {
throw new EncogError(e);
}
}
/**
* Load an object and handle reference if needed.
* @param objectField The field.
* @param parent The object that holds the field.
* @return The loaded object.
* @throws InstantiationException An error.
* @throws IllegalAccessException An error.
*/
private Object loadObject(final Field objectField, final Object parent)
throws InstantiationException, IllegalAccessException {
final String ref = this.in.getTag().getAttributeValue("ref");
// handle ref
if (ref != null) {
final int ref2 = Integer.parseInt(this.in.getTag()
.getAttributeValue("ref"));
this.mapper.addFieldMapping(ref2, objectField, parent);
this.in.readToTag();
return null;
} else {
final Class< ? > c = ReflectionUtil.resolveEncogClass(this.in
.getTag().getName());
if (c == null) {
throw new PersistError("Can't create class: "
+ this.in.getTag().getName());
}
final Object obj = c.newInstance();
loadActualObject(objectField, obj);
return obj;
}
}
/**
* Set a field value.
* @param field The field to set.
* @param target The object that contains the field.
* @param value The field value.
*/
private void setFieldValue(final Field field, final Object target,
final String value) {
try {
final Class< ? > type = field.getType();
if( type.isEnum() ) {
field.set(target, ReflectionUtil.resolveEnum(field, value));
}
else if (type == long.class) {
field.setLong(target, Long.parseLong(value));
} else if (type == int.class) {
field.setInt(target, Integer.parseInt(value));
} else if (type == short.class) {
field.setShort(target, Short.parseShort(value));
} else if (type == double.class) {
field.setDouble(target, Double.parseDouble(value));
} else if (type == float.class) {
field.setDouble(target, Float.parseFloat(value));
} else if (type == String.class) {
field.set(target, value);
} else if (type == boolean.class) {
field.setBoolean(target,
value.equalsIgnoreCase("true") ? Boolean.TRUE
: Boolean.FALSE);
}
} catch (final IllegalAccessException e) {
throw new PersistError("Error parsing field:" + field.getName(),e);
} catch (final NumberFormatException e) {
throw new PersistError("Error on field:" + field.getName(),e);
}
}
}