/*
* 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.File;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.encog.Encog;
import org.encog.neural.networks.BasicNetwork;
import org.encog.persist.location.FilePersistence;
import org.encog.persist.location.PersistenceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An EncogPersistedCollection holds a collection of EncogPersistedObjects. This
* allows the various neural networks and some data sets to be persisted. They
* are persisted to an XML form.
*
* The EncogPersistedCollection does not load the object into memory at once.
* This allows it to manage large files.
*
* @author jheaton
*
*/
public class EncogPersistedCollection implements EncogCollection {
/**
* The location this collection is saved at.
*/
private PersistenceLocation location;
/**
* Generic error message for bad XML.
*/
public static final String GENERAL_ERROR = "Malformed XML near tag: ";
/**
* The type is TextData.
*/
public static final String TYPE_TEXT = "TextData";
/**
* The type is PropertyData.
*/
public static final String TYPE_PROPERTY = "PropertyData";
/**
* The type is BasicNetwork.
*/
public static final String TYPE_BASIC_NET = "BasicNetwork";
/**
* The type is BasicLayer.
*/
public static final String TYPE_BASIC_LAYER = "BasicLayer";
/**
* The type is ContextLayer.
*/
public static final String TYPE_CONTEXT_LAYER = "ContextLayer";
/**
* The type is RadialBasisFunctionLayer.
*/
public static final String TYPE_RADIAL_BASIS_LAYER = "RadialBasisFunctionLayer";
/**
* The type is TrainingData.
*/
public static final String TYPE_TRAINING = "TrainingData";
/**
* The type is WeightedSynapse.
*/
public static final String TYPE_WEIGHTED_SYNAPSE = "WeightedSynapse";
/**
* The type is WeightlessSynapse.
*/
public static final String TYPE_WEIGHTLESS_SYNAPSE = "WeightlessSynapse";
/**
* The type is DirectSynapse.
*/
public static final String TYPE_DIRECT_SYNAPSE = "DirectSynapse";
/**
* The type is OneToOneSynapse.
*/
public static final String TYPE_ONE2ONE_SYNAPSE = "OneToOneSynapse";
/**
* The type is ParseTemplate.
*/
public static final String TYPE_NORMALIZATION = "DataNormalization";
/**
* The name attribute.
*/
public static final String ATTRIBUTE_NAME = "name";
/**
* The description attribute.
*/
public static final String ATTRIBUTE_DESCRIPTION = "description";
/**
* The logging object.
*/
private static final Logger LOGGER = LoggerFactory
.getLogger(EncogPersistedCollection.class);
/**
* For training continuation.
*/
public static final String TYPE_TRAINING_CONTINUATION = "TrainingContinuation";
/**
* The basic population.
*/
public static final String TYPE_POPULATION = "BasicPopulation";
public static final String TYPE_SVM = "SVMNetwork";
public static final String TYPE_BINARY = "BufferedNeuralDataSet";
public static final String TYPE_SCRIPT = "EncogScript";
/**
* Throw and log an error.
*
* @param tag
* The tag this error is for.
*/
public static void throwError(final String tag) {
final String str = EncogPersistedCollection.GENERAL_ERROR + tag;
if (EncogPersistedCollection.LOGGER.isErrorEnabled()) {
EncogPersistedCollection.LOGGER.error(str);
}
throw new PersistError(str);
}
/**
* The primary file being persisted to.
*/
private final PersistenceLocation filePrimary;
/**
* The temp file, to be used for merges.
*/
private PersistenceLocation fileTemp;
/**
* A writer to use with the file.
*/
@SuppressWarnings("unused")
private PersistWriter writer;
/**
* The platform this collection was created on.
*/
private String platform = "Java";
/**
* The version of the persisted file.
*/
private int fileVersion = 1;
/**
* Directory entries for all of the objects in the current file.
*/
private final Set<DirectoryEntry> directory = new TreeSet<DirectoryEntry>();
/**
* The version of Encog.
*/
private String encogVersion = Encog.getInstance().getProperties().get(
Encog.ENCOG_VERSION);
/**
* Create a persistance collection for the specified file.
*
* @param file
* The file to load/save.
*/
public EncogPersistedCollection(final File file) {
this(new FilePersistence(file));
}
/**
* Create an object based on the specified location.
*
* @param location
* The location to load/save from.
*/
public EncogPersistedCollection(final PersistenceLocation location) {
this.filePrimary = location;
if (this.filePrimary instanceof FilePersistence) {
final File file = ((FilePersistence) this.filePrimary).getFile();
String f = file.getAbsolutePath();
final int index = f.lastIndexOf('.');
if (index != -1) {
f = f.substring(0, index);
}
f += ".tmp";
this.fileTemp = new FilePersistence(new File(f));
if (this.filePrimary.exists()) {
try {
buildDirectory();
} catch (final PersistError e) {
create();
}
} else {
create();
}
} else {
this.fileTemp = null;
}
}
/**
* Construct an object with the specified filename.
*
* @param filename
* The filename to load/save from.
*/
public EncogPersistedCollection(final String filename) {
this(new File(filename));
}
/**
* Add an EncogPersistedObject to the collection.
*
* @param name
* The name of the object to load.
* @param obj
* The object to add.
*/
public void add(final String name, final EncogPersistedObject obj) {
if( obj instanceof BasicNetwork )
{
((BasicNetwork)obj).getStructure().updateFlatNetwork();
}
obj.setName(name);
final PersistWriter writer = new PersistWriter(this.fileTemp);
writer.begin();
writer.writeHeader();
writer.beginObjects();
writer.writeObject(obj);
writer.mergeObjects(this.filePrimary, name);
writer.endObjects();
writer.end();
writer.close();
mergeTemp();
buildDirectory();
}
/**
* Build a directory of objects. Also load the header information.
*/
public void buildDirectory() {
PersistReader reader = null;
try {
reader = new PersistReader(this.filePrimary);
final Map<String, String> header = reader.readHeader();
if (header != null) {
this.fileVersion = Integer.parseInt(header.get("fileVersion"));
this.encogVersion = header.get("encogVersion");
this.platform = header.get("platform");
}
final Set<DirectoryEntry> d = reader.buildDirectory();
this.directory.clear();
this.directory.addAll(d);
} finally {
if (reader != null) {
reader.close();
}
}
}
/**
* Clear the collection.
*/
public void clear() {
}
/**
* Create the file.
*/
public void create() {
final PersistWriter writer = new PersistWriter(this.filePrimary);
writer.begin();
writer.writeHeader();
writer.beginObjects();
writer.endObjects();
writer.end();
writer.close();
this.directory.clear();
}
/**
* Delete the specified object, use a directory entry.
*
* @param d
* The object to delete.
*/
public void delete(final DirectoryEntry d) {
this.delete(d.getName());
}
/**
* Delete the specified object.
*
* @param obj
* The object to delete.
*/
public void delete(final EncogPersistedObject obj) {
delete(obj.getName());
}
/**
* Delete the specified object.
*
* @param name
* the object name.
*/
public void delete(final String name) {
final PersistWriter writer = new PersistWriter(this.fileTemp);
writer.begin();
writer.writeHeader();
writer.beginObjects();
writer.mergeObjects(this.filePrimary, name);
writer.endObjects();
writer.end();
writer.close();
mergeTemp();
for (final DirectoryEntry d : this.directory) {
if (d.getName().equals(name)) {
this.directory.remove(d);
break;
}
}
}
/**
* Find the specified object, using a DirectoryEntry.
*
* @param d
* The directory entry to find.
* @return The loaded object.
*/
public EncogPersistedObject find(final DirectoryEntry d) {
return find(d.getName());
}
/**
* Called to search all Encog objects in this collection for one with a name
* that passes what was passed in.
*
* @param name
* The name we are searching for.
* @return The Encog object with the correct name.
*/
public EncogPersistedObject find(final String name) {
final PersistReader reader = new PersistReader(this.filePrimary);
final EncogPersistedObject result = reader.readObject(name);
reader.close();
if( result!=null )
result.setCollection(this);
return result;
}
/**
* @return The directory entries for the objects in this file.
*/
public Collection<DirectoryEntry> getDirectory() {
return this.directory;
}
/**
* @return the encogVersion
*/
public String getEncogVersion() {
return this.encogVersion;
}
/**
* @return the fileVersion
*/
public int getFileVersion() {
return this.fileVersion;
}
/**
* @return the platform
*/
public String getPlatform() {
return this.platform;
}
/**
* Merge the temp file with the main one, call this to make any changes
* permanent.
*/
public void mergeTemp() {
this.filePrimary.delete();
this.fileTemp.renameTo(this.filePrimary);
}
/**
* Update any header properties for an Encog object, for example, a rename.
*
* @param name
* The name of the object to change.
* @param newName
* The new name of this object.
* @param newDesc
* The description for this object.
*/
public void updateProperties(final String name, final String newName,
final String newDesc) {
final PersistWriter writer = new PersistWriter(this.fileTemp);
writer.begin();
writer.writeHeader();
writer.beginObjects();
writer.modifyObject(this.filePrimary, name, newName, newDesc);
writer.endObjects();
writer.end();
writer.close();
mergeTemp();
buildDirectory();
}
/**
* Determine if the specified resource exists.
* @param name The name of the resource to check.
* @return True if it exists.
*/
public boolean exists(String name) {
for( DirectoryEntry dir: this.directory )
{
if( dir.getName().equals(name))
return true;
}
return false;
}
public PersistenceLocation getLocation()
{
return this.location;
}
}