code: package dan2097.org.bitbucket.reactionextraction;
  
  import static dan2097.org.bitbucket.utility.ChemicalTaggerTags.*;
  
  import java.util.ArrayDeque;
  import java.util.ArrayList;
  import java.util.Deque;
  import java.util.List;
  
  import org.apache.log4j.Logger;
  
  import nu.xom.Element;
  import nu.xom.Elements;
  
  public class ChemTaggerOutputNameExtraction {
  	
  	private static final Logger LOG = Logger.getLogger(ChemTaggerOutputNameExtraction.class);
  
  	/**
  	 * Finds the chemical name of the chemical described by a chemical tagger MOLECULE_Container/UNNAMEDMOLECULE_Container
  	 * In the case of a mixture e.g. octanol-water the returned list will contain multiple entries
  	 * @param moleculeOrUnnamedMolecule
  	 * @return
  	 */
  	public static List<String> findMoleculeName(Element moleculeOrUnnamedMolecule) {
  		String elName = moleculeOrUnnamedMolecule.getLocalName();
  		if (elName.equals(MOLECULE_Container)) {
  			Element oscarCM = moleculeOrUnnamedMolecule.getFirstChildElement(OSCARCM_Container);
  			if (oscarCM == null){
  				LOG.warn("malformed Molecule, no child OSCAR-CM: " + moleculeOrUnnamedMolecule.toXML());
  				List<String> nameComponents = new ArrayList<String>();
  				nameComponents.add("");
  				return nameComponents;
  			}
  			return findMoleculeNameFromOscarCM(oscarCM);
  		}
  		else if (elName.equals(UNNAMEDMOLECULE_Container)) {
  			String name = findMoleculeNameFromUnnamedMoleculeEl(moleculeOrUnnamedMolecule);
  			List<String> nameComponents = new ArrayList<String>();
  			nameComponents.add(name);
  			return nameComponents;
  		}
  		throw new IllegalArgumentException("Unexpected tag type:" + elName +" The following are allowed" +
  				MOLECULE_Container + ", "+ UNNAMEDMOLECULE_Container);
  	}
  	
  	
  	/**
  	 * Finds the chemical name of the chemical described by a chemical tagger MOLECULE tag
  	 * In the case of a mixture e.g. octanol-water the returned list will contain multiple entries
  	 * @param molecule
  	 * @return
  	 */
  	static List<String> findMoleculeNameFromOscarCM(Element oscarCM) {
  		if (oscarCM == null){
  			throw new IllegalArgumentException("Input oscarCM was null");
  		}
  		Elements words = oscarCM.getChildElements();
  		List<String> nameComponents = new ArrayList<String>();
  		StringBuilder builder = new StringBuilder();
  		for (int i = 0; i < words.size(); i++) {
  			Element word = words.get(i);
  			if (word.getLocalName().equals(OSCAR_CM)){
  				builder.append(words.get(i).getValue());
  				builder.append(" ");
  			}
  			else{
  				String nameComponent = builder.toString().trim();
  				if (nameComponent.length() > 0){
  					nameComponents.add(nameComponent);
  					builder = new StringBuilder();
  				}
  			}
  		}
  		String nameComponent = builder.toString().trim();
  		if (nameComponent.length() > 0){
  			nameComponents.add(nameComponent);
  		}
  		return nameComponents;
  	}
  	
  	/**
  	 * Returns the space concatenated values of the first set of adjective* noun+
  	 * @param molecule
  	 * @return
  	 */
  	static String findMoleculeNameFromUnnamedMoleculeEl(Element unnamedMoleculeEl) {
  		StringBuilder builder = new StringBuilder();
  		Elements children = unnamedMoleculeEl.getChildElements();
  		boolean foundStartOfName = false;
  		for (int i = 0; i < children.size(); i++) {
  			Element elToConsider = children.get(i);
  			String localName = elToConsider.getLocalName();
  			if (localName.startsWith("JJ") || localName.startsWith("NN")
  					|| localName.equals(REFERENCETOCOMPOUND_Container)
  					|| localName.equals(OSCAR_ASE)){
  				foundStartOfName = true;
  				builder.append(getStringContent(elToConsider));
  			}
  			else if (foundStartOfName) {
  				break;
  			}
  		}
  		if (builder.length() == 0){
  			return null;
  		}
  		builder.deleteCharAt(builder.length() - 1);
  		return builder.toString();
  	}
  	
  	/**
  	 * Uses a stack to iteratively enumerate and space concatenate the given element and its descendants in document order
  	 * @param startingEl
  	 * @return
  	 */
  	private static StringBuilder getStringContent(Element startingEl) {
  		StringBuilder builder = new StringBuilder();
  		Deque<Element> stack = new ArrayDeque<Element>();
  		stack.add(startingEl);
  		while (!stack.isEmpty()) {
  			Element currentEl = stack.removeFirst();
  			Elements els = currentEl.getChildElements();
  			if (els.size() > 0){
  				for (int i = 0; i < els.size(); i++) {
  					stack.add(els.get(i));
  				}
  			}
  			else{
  				builder.append(currentEl.getValue());
  				builder.append(' ');
  			}
  		}
  		return builder;
  	}
  }
  