/* * 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; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; import org.encog.parse.tags.Tag.Type; import org.encog.parse.tags.read.ReadXML; import org.encog.parse.tags.write.WriteXML; import org.encog.persist.location.PersistenceLocation; import org.encog.persist.persistors.PersistorUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class for reading Encog persited object files. * * @author jheaton * */ public class PersistReader { /** * The name attribute. */ public static final String ATTRIBUTE_NAME = "name"; /** * The objects tag. */ public static final String TAG_OBJECTS = "Objects"; /** * The XML reader. */ private final ReadXML in; /** * The input stream. */ private final InputStream fileInput; /** * The logging object. */ private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * Construct a persist reader. * * @param location * The location to use. */ public PersistReader(final PersistenceLocation location) { this.fileInput = location.createInputStream(); this.in = new ReadXML(this.fileInput); } /** * Advance to the specified object. * * @param name * The name of the object looking for. * @return The beginning element of the object found. */ public boolean advance(final String name) { advanceObjectsCollection(); return advanceObjects(name); } /** * Once you are in the objects collection, advance to a specific object. * * @param name * The name of the object to advance to. * @return The beginning tag of that object if its found, null otherwise. */ private boolean advanceObjects(final String name) { while (this.in.readToTag()) { final Type type = this.in.getTag().getType(); if (type == Type.BEGIN) { final String elementName = this.in.getTag().getAttributeValue( "name"); if ((elementName != null) && elementName.equals(name)) { return true; } else { skipObject(); } } } return false; } /** * Advance to the objects collection. */ public void advanceObjectsCollection() { while (this.in.readToTag()) { final Type type = this.in.getTag().getType(); if ((type == Type.BEGIN) && this.in.getTag().getName().equals( PersistReader.TAG_OBJECTS)) { return; } } final String str = "Can't find objects collection, invalid file."; if (this.logger.isErrorEnabled()) { this.logger.error(str); } throw new PersistError(str); } /** * Advance to the specified tag. * * @param tag * The tag to search for. * @return True if the tag was found. */ private boolean advanceToTag(final String tag) { while (this.in.readToTag()) { final Type type = this.in.getTag().getType(); if (type == Type.BEGIN) { if (this.in.getTag().getName().equals(tag)) { return true; } else { skipObject(); } } } return false; } /** * Build a directory entry list for the file. * * @return A list of objects in the file. */ public Set<DirectoryEntry> buildDirectory() { final Set<DirectoryEntry> result = new TreeSet<DirectoryEntry>(); advanceObjectsCollection(); while (this.in.readToTag()) { if (this.in.is(PersistReader.TAG_OBJECTS, false)) { break; } final String type = this.in.getTag().getName(); final String name = this.in.getTag().getAttributeValue("name"); final String description = this.in.getTag().getAttributeValue( "description"); final DirectoryEntry entry = new DirectoryEntry(type, name, description); result.add(entry); skipObject(); } return result; } /** * Close the file. */ public void close() { try { this.fileInput.close(); } catch (final IOException e) { throw new PersistError(e); } } /** * Copy all of the attributes to the writer. * * @param out * The XML writer. * @param replace * A map of attributes to replace. This allows new values to be * specified for select attributes. */ private void copyAttributes(final WriteXML out, final Map<String, String> replace) { for (final String key : this.in.getTag().getAttributes().keySet()) { String value = this.in.getTag().getAttributeValue(key); if ((replace != null) && replace.containsKey(key)) { value = replace.get(key); } out.addAttribute(key, value); } } /** * Copy an XML object, no need to know what it contains, just copy it. This * way we will not damage unknown objects during a merge. * * @param out * The XML writer. * @param replace * A map of attributes to replace. This allows new values to be * specified for select attributes. */ private void copyXML(final WriteXML out, final Map<String, String> replace) { final StringBuilder text = new StringBuilder(); int depth = 0; int ch; copyAttributes(out, replace); final String contain = this.in.getTag().getName(); out.beginTag(contain); while ((ch = this.in.read()) != -1) { final Type type = this.in.getTag().getType(); if (ch == 0) { if (type == Type.BEGIN) { if (text.length() > 0) { out.addText(text.toString()); text.setLength(0); } copyAttributes(out, null); out.beginTag(this.in.getTag().getName()); depth++; } else if (type == Type.END) { if (text.length() > 0) { out.addText(text.toString()); text.setLength(0); } if (depth == 0) { break; } else { out.endTag(this.in.getTag().getName()); } depth--; } } else { text.append((char) ch); } } out.endTag(contain); } /** * @return The ReadXML object being used by this object. */ public ReadXML getXMLInput() { return this.in; } /** * Obtain the Encog header from the file. * * @return Name value pair map of the header attributes. */ public Map<String, String> readHeader() { Map<String, String> headers = null; if (advanceToTag("Document")) { if (advanceToTag("Header")) { headers = this.in.readPropertyBlock(); } } return headers; } /** * Read until the next tag of the specified name. * * @param name * The name searched for. * @return True if the tag was found. */ public boolean readNextTag(final String name) { while (this.in.readToTag()) { final Type type = this.in.getTag().getType(); if (type == Type.BEGIN) { if (this.in.getTag().getName().equals(name)) { return true; } else { skipObject(); } } } return false; } /** * Read all text until the specified ending tag is found. * * @param name * The tag. * @return The text found. */ public String readNextText(final String name) { final StringBuilder result = new StringBuilder(); return result.toString(); } /** * Read the specific object, search through the objects until its found. * * @param name * The name of the object you are looking for. * @return The object found, null if not found. */ public EncogPersistedObject readObject(final String name) { // did we find the object? if (advance(name)) { final String objectType = this.in.getTag().getName(); final Persistor persistor = PersistorUtil .createPersistor(objectType); if (persistor == null) { throw new PersistError("Do not know how to load: " + objectType); } return persistor.load(this.in); } else { return null; } } /** * Read the value in a period delimited string. For example * property.name.value. * * @param name * The property to read. * @return The value found at the specified property. */ public String readValue(final String name) { final StringTokenizer tok = new StringTokenizer(name, "."); while (tok.hasMoreTokens()) { final String subName = tok.nextToken(); if (!readNextTag(subName)) { return null; } } return readNextText(this.in.getTag().getName()); } /** * Modify the properties of this object. * * @param out * The XML writer. * @param targetName * The name of the object to change. * @param newName * The new name of this object. * @param newDesc * The new description of this object. */ public void saveModified(final WriteXML out, final String targetName, final String newName, final String newDesc) { advanceObjectsCollection(); while (this.in.readToTag()) { final Type type = this.in.getTag().getType(); if (type == Type.BEGIN) { final String name = this.in.getTag().getAttributeValue( PersistReader.ATTRIBUTE_NAME); if (name.equals(targetName)) { final Map<String, String> replace = new HashMap<String, String>(); replace.put("name", newName); replace.put("description", newDesc); copyXML(out, replace); } else { copyXML(out, null); } } } } /** * Save all objects to the specified steam, skip the one specified by the * skip parameter. Do not attempt to understand the structure, just copy. * * @param out * The XML writer to save the objects to. * @param skip * The object to skip. */ public void saveTo(final WriteXML out, final String skip) { advanceObjectsCollection(); while (this.in.readToTag()) { final Type type = this.in.getTag().getType(); if (type == Type.BEGIN) { final String name = this.in.getTag().getAttributeValue( PersistReader.ATTRIBUTE_NAME); if (name.equals(skip)) { skipObject(); } else { copyXML(out, null); } } } } /** * Skip the current specified object. */ private void skipObject() { int depth = 0; while (this.in.readToTag()) { final Type type = this.in.getTag().getType(); switch (type) { case END: if (depth == 0) { return; } depth--; break; case BEGIN: depth++; break; } } } }