class Parser {

	List<Element> parse(NameToStructureConfig n2sConfig, String name) throws ParsingException {
		Integer[] componentRatios = null;
		if (name.endsWith(")") || name.endsWith("]") || name.endsWith("}")){
			Matcher m = matchStoichiometryIndication.matcher(name);
			if (m.find()){
				componentRatios = processStoichiometryIndication(m.group());
				name = m.replaceAll("");
			}
		}
		Parse parse = null;
		if (name.contains(", ")){
			try{
				TokenizationResult tokenizationResult = tokeniser.tokenize(CASTools.uninvertCASName(name, parseRules), false);
				if (tokenizationResult.isSuccessfullyTokenized()){
					parse = tokenizationResult.getParse();
				}
			}
			catch (ParsingException ignored) {
			}
		}
		else if (name.contains("; ")){//a mixture, spaces are sufficient for OPSIN to treat as a mixture. These spaces for obvious reasons must not be removed
			TokenizationResult tokenizationResult = tokeniser.tokenize(matchSemiColonSpace.matcher(name).replaceAll(" "), false);
			if (tokenizationResult.isSuccessfullyTokenized()){
				parse = tokenizationResult.getParse();
			}
		}
		boolean allowSpaceRemoval;
		if (parse == null) {
			allowSpaceRemoval = true;
			TokenizationResult tokenizationResult = tokeniser.tokenize(name , true);
			if (tokenizationResult.isSuccessfullyTokenized()){
				parse = tokenizationResult.getParse();
			}
			else{
				if (n2sConfig.isDetailedFailureAnalysis()){
					generateExactParseFailureReason(tokenizationResult, name);
				}
				else{
					throw new ParsingException(name + " is unparsable due to the following being uninterpretable: " + tokenizationResult.getUninterpretableName()
							+ " The following was not parseable: " +tokenizationResult.getUnparsableName());
				}
			}
		}
		else {
			allowSpaceRemoval = false;
		}
		
		List<Parse> parses = generateParseCombinations(parse);
		if (parses.size()==0) {
			throw new ParsingException("No parses could be found for " + name);
		}
		
		List<Element> results = new ArrayList<Element>();
		ParsingException preciseException = null;
		for(Parse pp : parses) {
			Element moleculeEl = new GroupingEl(MOLECULE_EL);
			moleculeEl.addAttribute(new Attribute(NAME_ATR, name));
			for(ParseWord pw : pp.getWords()) {
				Element word = new GroupingEl(WORD_EL);
				moleculeEl.addChild(word);
				if (pw.getParseTokens().size() != 1){
					throw new ParsingException("OPSIN bug: parseWord should have exactly 1 annotations after creating additional parses step");
				}
				ParseTokens tokensForWord = pw.getParseTokens().get(0);
				WordType wordType = OpsinTools.determineWordType(tokensForWord.getAnnotations());
				word.addAttribute(new Attribute(TYPE_ATR, wordType.toString()));
				if (pw.getWord().startsWith("-")){//we want -functionalterm to be the same as functionalterm
					word.addAttribute(new Attribute(VALUE_ATR, pw.getWord().substring(1)));
				}
				else{
					word.addAttribute(new Attribute(VALUE_ATR, pw.getWord()));
				}
				writeWordXML(word, tokensForWord.getTokens(), WordTools.chunkAnnotations(tokensForWord.getAnnotations()));
			}
			/* All words are placed into a wordRule.
			 * Often multiple words in the same wordRule.
			 * WordRules can be nested within each other e.g. in Carbonyl cyanide m-chlorophenyl hydrazone ->
			 * <wr><wr>Carbonyl cyanide</wr> m-chlorophenyl hydrazone </wr>
			 */
			try {
				wordRules.groupWordsIntoWordRules(moleculeEl, n2sConfig, allowSpaceRemoval, componentRatios);
			} catch (ParsingException e) {
				if(LOG.isDebugEnabled()) {
					LOG.debug(e.getMessage(), e);
				}
				// Using that parse no word rules matched
				continue;
			}
			try{
				if (componentRatios != null){
					applyStoichiometryIndicationToWordRules(moleculeEl, componentRatios);
				}
				if (moleculeEl.getAttributeValue(ISSALT_ATR) != null && moleculeEl.getChildElements(WORDRULE_EL).size() < 2) {
					throw new ParsingException(name + " is apparently a salt, but the name only contained one component. The name could be describing a class of compounds");
				}
				results.add(moleculeEl);
			} catch (ParsingException e) {
				preciseException = e;
			}
	
		}
		if (results.size() == 0) {
			if (preciseException != null) {
				throw preciseException;
			}
			throw new ParsingException(name + " could be parsed but OPSIN was unsure of the meaning of the words. This error will occur, by default, if a name is just a substituent");
		}
		
		return results;
	}

	  Parser();
	  Parser(WordRules wordRules, Tokeniser tokeniser, ResourceManager resourceManager);

}