package org.nexml.model.impl; import java.util.HashSet; import java.util.Set; import org.nexml.model.CharacterState; import org.nexml.model.CharacterStateSet; import org.nexml.model.CompoundCharacterState; import org.nexml.model.PolymorphicCharacterState; import org.nexml.model.UncertainCharacterState; import org.w3c.dom.Document; import org.w3c.dom.Element; class CharacterStateSetImpl extends SetManager<CharacterState> implements CharacterStateSet { Set<CharacterState> mCharacterStates = new HashSet<CharacterState>(); private UncertainCharacterState mMissing; private UncertainCharacterState mGap; /** * Protected constructors that take a DOM document object but not * an element object are used for generating new element nodes in * a NeXML document. On calling such constructors, a new element * is created, which can be retrieved using getElement(). After this * step, the Impl class that called this constructor would still * need to attach the element in the proper location (typically * as a child element of the class that called the constructor). * @param document a DOM document object * @author rvosa */ protected CharacterStateSetImpl(Document document) { super(document); } /** * Protected constructors are intended for recursive parsing, i.e. * starting from the root element (which maps onto DocumentImpl) we * traverse the element tree such that for every child element that maps * onto an Impl class the containing class calls that child's protected * constructor, passes in the element of the child. From there the * child takes over, populates itself and calls the protected * constructors of its children. These should probably be protected * because there is all sorts of opportunity for outsiders to call * these in the wrong context, passing in the wrong elements etc. * @param document the containing DOM document object. Every Impl * class needs a reference to this so that it can create DOM element * objects * @param element the equivalent NeXML element (e.g. for OTUsImpl, it's * the <otus/> element) * @author rvosa */ protected CharacterStateSetImpl(Document document,Element element) { super(document,element); } /* * (non-Javadoc) * @see org.nexml.model.impl.NexmlWritableImpl#getTagName() */ @Override String getTagName() { return getTagNameClass(); } static String getTagNameClass() { return "states"; } /* * (non-Javadoc) * @see org.nexml.model.CharacterStateSet#getCharacterStates() */ public Set<CharacterState> getCharacterStates() { return mCharacterStates; } /* * (non-Javadoc) * @see org.nexml.model.CharacterStateSet#setCharacterStates(java.util.Set) */ public void setCharacterStates(Set<CharacterState> characterStates) { mCharacterStates = characterStates; } /** * This method creates the state element (i.e. a single state definition * within a state set, inside a format element). Because state elements * require a symbol attribute it needs to be passed in here. * @author rvosa */ public CharacterState createCharacterState(Object symbol) { CharacterStateImpl characterStateImpl = null; if ( null != symbol && symbol.toString().equals("?") ) { mMissing = createUncertainCharacterState(symbol, getCharacterStates()); return mMissing; } else if ( null != symbol && symbol.toString().equals("-") ) { Set<CharacterState> members = new HashSet<CharacterState>(); mGap = createUncertainCharacterState(symbol, members); addStateToMissing(mGap); return mGap; } else { characterStateImpl = new CharacterStateImpl(getDocument()); getElement().appendChild(characterStateImpl.getElement()); characterStateImpl.setSymbol(symbol); getCharacterStates().add(characterStateImpl); addStateToMissing(characterStateImpl); return characterStateImpl; } } protected void addStateToMissing(CharacterState state) { if ( null != mMissing ) { Set<CharacterState> currentMembersOfMissing = mMissing.getStates(); currentMembersOfMissing.add(state); mMissing.setStates(currentMembersOfMissing); } } protected CharacterState createCharacterState(Element stateElement) { CharacterStateImpl characterStateImpl = new CharacterStateImpl(getDocument(),stateElement); characterStateImpl.setSymbol(stateElement.getAttribute("symbol")); getCharacterStates().add(characterStateImpl); return characterStateImpl; } /** * The method creates the polymorphic_state_set element. Because state * elements require a symbol attribute it needs to be passed in here. * Polymorphic_state_set elements have two or more members, these need * to be passed in here. XXX In discussion with Jeet, we concluded that * polymorphic state sets biologically are a combination of fundamental * states (whereas uncertain state sets can also contain polymorphic states). * THis, however, is very much open to debate. In any case, the method * at present doesn't distinguish between CharacterState subclasses, but * perhaps that needs to change. * @author rvosa */ public PolymorphicCharacterState createPolymorphicCharacterState( Object symbol, Set<CharacterState> members) { PolymorphicCharacterStateImpl polymorphicCharacterStateImpl = new PolymorphicCharacterStateImpl(getDocument()); polymorphicCharacterStateImpl.setSymbol(symbol); polymorphicCharacterStateImpl.setStates(members); getCharacterStates().add(polymorphicCharacterStateImpl); return polymorphicCharacterStateImpl; } protected PolymorphicCharacterState createPolymorphicCharacterState(Element element) { PolymorphicCharacterStateImpl polymorphicCharacterStateImpl = new PolymorphicCharacterStateImpl(getDocument(),element); populateCompoundCharacterState(polymorphicCharacterStateImpl, element); getCharacterStates().add(polymorphicCharacterStateImpl); return polymorphicCharacterStateImpl; } private void populateCompoundCharacterState(CompoundCharacterState state, Element element) { state.setSymbol(element.getAttribute("symbol")); Set<CharacterState> memberStates = new HashSet<CharacterState>(); for ( Element memberElement : getChildrenByTagName(element, "member") ) { String memberId = memberElement.getAttribute("state"); CharacterState memberState = lookupCharacterStateById(memberId); if ( null == memberState ) { throw new RuntimeException("Can't find member state with id "+memberId); } else { memberStates.add(memberState); } } state.setStates(memberStates); } /** * XXX see discussion for createPolymorphicCharacterState() */ public UncertainCharacterState createUncertainCharacterState( Object symbol, Set<CharacterState> members) { UncertainCharacterStateImpl uncertainCharacterStateImpl = new UncertainCharacterStateImpl(getDocument()); getElement().appendChild(uncertainCharacterStateImpl.getElement()); uncertainCharacterStateImpl.setSymbol(symbol); uncertainCharacterStateImpl.setStates(members); getCharacterStates().add(uncertainCharacterStateImpl); return uncertainCharacterStateImpl; } protected UncertainCharacterState createUncertainCharacterState(Element element) { UncertainCharacterState uncertainCharacterStateImpl = new UncertainCharacterStateImpl(getDocument(),element); populateCompoundCharacterState(uncertainCharacterStateImpl, element); getCharacterStates().add(uncertainCharacterStateImpl); return uncertainCharacterStateImpl; } /** * Makes working with predefined sets, (e.g. molecular) easier by allowing searching for states * @author rvosa */ public CharacterState lookupCharacterStateById(String id) { if (id == null) { return null; } for (CharacterState cs : getCharacterStates()){ if (id.equals(cs.getId())){ return cs; } } return null; } /** * Makes working with predefined sets, (e.g. molecular) easier by allowing searching for states * @author pmidford */ public CharacterState lookupCharacterStateByLabel(String label){ if (label == null){ return null; } for (CharacterState cs : getCharacterStates()){ if (label.equals(cs.getLabel())){ return cs; } } return null; } /** * Makes working with predefined sets, (e.g. molecular) easier by allowing searching for states * @author pmidford */ public CharacterState lookupCharacterStateBySymbol(String symbol){ if (symbol == null){ return null; } for (CharacterState cs : getCharacterStates()){ if (symbol.equals(cs.getSymbol())){ return cs; } } return createCharacterState(symbol); } }