/* -*- c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/*
* FreeDots -- MusicXML to braille music transcription
*
* Copyright 2008-2010 Mario Lang All Rights Reserved.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details (a copy is included in the LICENSE.txt file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License
* along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This file is maintained by Mario Lang <mlang@delysid.org>.
*/
package freedots.braille;
import java.util.Iterator;
/** Represents a logical unit composed of several smaller objects.
*/
public class BrailleList extends java.util.LinkedList<BrailleSequence>
implements BrailleSequence {
/** Constructs a new BrailleList object.
*/
public BrailleList() { super(); }
private BrailleList parent = null;
/** Returns the parent of this {@code BrailleList}.
* @return {@code null} if this {@code BrailleList} has not been added to a
* parent container yet.
*/
public final BrailleList getParent() { return parent; }
public final void setParent(final BrailleList parent) {
if (parent == null) throw new NullPointerException();
if (this.parent != null)
throw new IllegalStateException("Parent already set");
this.parent = parent;
}
/** Append a sign.
* This method takes care of inserting {@link GuideDot} objects if
* required.
* @see BrailleSequence#needsGuideDot
*/
@Override public boolean add(final BrailleSequence item) {
if (!isEmpty() && getLast().needsGuideDot(item)) {
final BrailleSequence dot = new GuideDot();
dot.setParent(this);
if (!super.add(dot)) return false;
}
item.setParent(this);
return super.add(item);
}
@Override public void addLast(final BrailleSequence item) { add(item); }
public String getDescription() {
return "Groups several signs as a logical unit";
}
/** Checks if the last element of this list needs a guide dot after it.
*/
public boolean needsGuideDot(BrailleSequence next) {
return !isEmpty() && getLast().needsGuideDot(next);
}
public Object getScoreObject() { return null; }
@Override public String toString() {
return this.appendTo(new StringBuilder()).toString();
}
public StringBuilder appendTo(StringBuilder sb) {
for (BrailleSequence seq: this) seq.appendTo(sb);
return sb;
}
public int length() {
int chars = 0;
for (BrailleSequence seq: this) chars += seq.length();
return chars;
}
public char charAt(int index) { return toString().charAt(index); }
public CharSequence subSequence(int start, int end) {
return toString().subSequence(start, end);
}
/** Retrieves the {@code Sign} at index.
*/
public Sign getSignAtIndex(final int index) {
final Iterator<BrailleSequence> iterator = iterator();
int pos = 0;
while (iterator.hasNext()) {
final BrailleSequence current = iterator.next();
final int length = current.length();
if (pos + length > index)
return (current instanceof BrailleList)
? ((BrailleList)current).getSignAtIndex(index - pos)
: (Sign)current;
else pos += length;
}
return null;
}
/** Retrieves the visual score object responsible for the braille at index.
* @see #getSignAtIndex
*/
public Object getScoreObjectAtIndex(final int index) {
for (BrailleSequence sign = getSignAtIndex(index); sign != null;
sign = sign.getParent()) {
final Object object = sign.getScoreObject();
if (object != null) return object;
}
return null;
}
/**
* @return -1 if the given score object was not found
*/
public int getIndexOfScoreObject(final Object scoreObject) {
if (scoreObject == null)
throw new NullPointerException("Trying to search for null");
final Iterator<BrailleSequence> iterator = iterator();
int index = 0;
while (iterator.hasNext()) {
final BrailleSequence current = iterator.next();
if (current.getScoreObject() == scoreObject) return index;
if (current instanceof BrailleList) {
final BrailleList compound = (BrailleList)current;
final int subIndex = compound.getIndexOfScoreObject(scoreObject);
if (subIndex >= 0) return index + subIndex;
}
index += current.length();
}
return -1;
}
/** Determines the string length from the last occurence of a class.
* <p>
* This method does a deep search.
* @return -1 if class was not found in this list or its children
*/
public final int lengthSince(Class<? extends BrailleSequence> seqClass) {
int length = 0;
java.util.ListIterator<BrailleSequence> iterator = listIterator(size());
while (iterator.hasPrevious()) {
BrailleSequence seq = iterator.previous();
if (seq.getClass() == seqClass) return length;
if (seq instanceof BrailleList) {
final int subLength = ((BrailleList)seq).lengthSince(seqClass);
if (subLength != -1) return length + subLength;
}
length += seq.length();
}
return -1;
}
}