ListData.java
// <editor-fold defaultstate="collapsed" desc="license">
/*
* Copyright (c) 2014, Karl H. Beckers
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the <ORGANIZATION> nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
**/
// </editor-fold>
package net.jarre_de_the.griffin.types.data;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.jarre_de_the.griffin.Util;
import net.jarre_de_the.griffin.exception.NoFieldFoundException;
import net.jarre_de_the.griffin.exception.NoFieldInListException;
import net.jarre_de_the.griffin.file.Gff;
import net.jarre_de_the.griffin.types.field.AbstractField;
import net.jarre_de_the.griffin.types.field.AbstractField.FieldType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of the LIST data type.
*
* @author charly4711
* @param <T> StructData or one of its child classes
*/
public class ListData<T extends StructData> extends AbstractData
implements Container, Cloneable {
private static final Logger LOGGER = LoggerFactory.getLogger(ListData.class);
private List<T> elements;
/*
*
* constructors
*
*/
public ListData() {
}
public ListData(List<T> elements) {
setValue(elements);
}
/*
*
* read from file
*
*/
public static ListData read(RandomAccessFile in,
Gff file) throws IOException {
LOGGER.debug("Reading ListData from file.");
// we're just hoping we'll never encounter a GFF file where a
// single buffer is > MAXINT
int size = DWordData.read(in).getValueAsNumber().intValue();
LOGGER.trace("Need to read " + size + " elements.");
List<StructData> readElements = new ArrayList<StructData>();
for (int i = 0; i < size; i++) {
long lOff = DWordData.read(in).getValueAsNumber();
LOGGER.trace("Reading element " + i + " from offset: " + lOff);
lOff *= StructData.STRUCT_LENGTH;
long sPos = in.getFilePointer();
in.seek(lOff + file.getStructOff() + file.getEmbeddedOffset());
readElements.add(StructData.read(in, file));
in.seek(sPos);
}
return new ListData(readElements);
}
/*
*
* setter
*
*/
private void setValue(List<T> elements) {
this.elements = elements;
}
/*
*
* getter
*
*/
public List<T> getValueAsList() {
return elements;
}
public T getValueElement(int i) {
if (elements != null && elements.size() > i) {
return elements.get(i);
} else {
return null;
}
}
public int getSize() {
if (elements == null) {
return 0;
}
return elements.size();
}
/*
*
* utility
*
*/
/**
* Implements Container
* <p>
* @param clazz
* @param label
* @return
*/
@Override
public <T extends AbstractField> List<FoundField<T>>
findField(Class<T> clazz, byte[] label) {
return findField(clazz, label, null, null);
}
@Override
public <T extends AbstractField, M extends AbstractData> List<FoundField<T>>
findField(Class<T> clazz, byte[] label, M value) {
return findField(clazz, label, value, null);
}
@Override
public <T extends AbstractField> List<FoundField<T>>
findField(Class<T> clazz, byte[] label, String regex) {
return findField(clazz, label, null, regex);
}
private <T extends AbstractField, M extends AbstractData> List<FoundField<T>>
findField(Class<T> clazz, byte[] label, M value, String regex) {
List<FoundField<T>> ret = new ArrayList<FoundField<T>>();
LOGGER.debug("Trying to find field "
+ Util.getNullTerminatedString(label)
+ " of type: " + (clazz == null ? "null" : clazz.getSimpleName()));
if (elements != null) {
if (elements.size() > 0) {
LOGGER.trace("Need to examine " + elements.size()
+ " fields.");
for (int i = 0; i < elements.size(); i++) {
StructData f = elements.get(i);
List<FoundField<T>> lf;
if (null != value) {
lf = f.findField(clazz, label, value);
} else if (null != regex) {
lf = f.findField(clazz, label, regex);
} else {
lf = f.findField(clazz, label);
}
if (lf != null) {
for (FoundField<T> foundField : lf) {
int[] localPath = new int[foundField.getPath().length + 1];
localPath[0] = i;
System.arraycopy(foundField.getPath(), 0, localPath, 1,
foundField.getPath().length);
ret.add(new FoundField<T>(foundField.getField(), localPath));
}
}
}
} else {
LOGGER.trace("No fields to examine.");
}
} else {
LOGGER.trace("No fields to examine.");
}
LOGGER.debug(ret.size() + " elements found.");
return ret;
}
/**
* Implements Container as a no-op because ListData cannot contain a Field
* directly.
* <p>
* @param clazz
* @param label
* @return
*/
@Override
public <T extends AbstractField> T findLocalField(Class<T> clazz, byte[] label) {
FieldType ft = null;
for (FieldType f : FieldType.values()) {
if (f.getFieldClassOfType().equals(clazz)) {
ft = f;
break;
}
}
throw new NoFieldFoundException(this, ft, label);
}
@Override
public AbstractField findFieldByPath(int[] path) {
if (path.length == 1) {
throw new NoFieldInListException(this, path);
}
if (path.length > 1) {
int here = path[0];
StructData sd = this.getValueElement(here);
if (sd != null) {
AbstractField tmp = sd.getValueElement(path[1]);
if (tmp != null && path.length == 2) {
return tmp;
} else if (tmp != null && (tmp.getValue() instanceof Container)) {
return ((Container) tmp.getValue()).findFieldByPath(Arrays.copyOfRange(path, 2, path.length));
}
}
}
throw new NoFieldFoundException(this, path);
}
@Override
public ListData clone() throws CloneNotSupportedException {
LOGGER.debug("Cloning object");
ListData clone = (ListData) super.clone();
if (null != elements) {
List<StructData> elem = new ArrayList<StructData>();
for (StructData s : elements) {
elem.add(s.clone());
}
clone.setValue(elem);
} else {
clone.setValue(null);
}
return clone;
}
@Override
public boolean equals(Object compare) {
if (compare == this) {
return true;
}
if (!(compare instanceof ListData)) {
return false;
}
ListData ld = (ListData) compare;
if (ld.getValueAsList() == null && elements == null) {
return true;
} else if ((ld.getValueAsList() == null && elements != null) || (ld.getValueAsList() != null && elements == null)) {
return false;
}
if (ld.getValueAsList().size() != elements.size()) {
return false;
}
boolean equals = true;
for (int i = 0; i < ld.getSize(); i++) {
if (!ld.getValueElement(i).equals(this.getValueElement(i))) {
equals = false;
break;
}
}
if (equals == true) {
return true;
}
return false;
}
@Override
public int hashCode() {
int result = this.getSize();
if (result > 0) {
result ^= Arrays.hashCode(elements.toArray());
}
return result;
}
}