private String getFirstMatchingAriaRoleFromTokenList(String tokenList)
            throws SAXException {
        if (tokenList == null || "".equals(tokenList)) {
            return "";
        }
        int len = tokenList.length();
        List<String> tokens = new LinkedList<>();
        boolean collectingSpace = true;
        int start = 0;
        for (int i = 0; i < len; i++) {
            char c = tokenList.charAt(i);
            if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
                if (!collectingSpace) {
                    tokens.add(tokenList.substring(start, i));
                    collectingSpace = true;
                }
            } else {
                if (collectingSpace) {
                    start = i;
                    collectingSpace = false;
                }
            }
        }
        if (start < len) {
            tokens.add(tokenList.substring(start, len));
        }
        String roleValue = null;
        List<String> unrecognizedTokens = new LinkedList<>();
        List<String> superfluousTokens = new LinkedList<>();
        for (String token : tokens) {
            if (Arrays.binarySearch(nonAbstractAriaRoles, token) < 0) {
                unrecognizedTokens.add(token);
            } else if (roleValue == null) {
                roleValue = token;
            } else {
                if (!"presentation".equals(token)) {
                    superfluousTokens.add(token);
                }
            }
        }
        if (errorHandler != null && roleValue != null
                && unrecognizedTokens.size() > 0) {
            errorHandler.error(new SAXParseException("Discarding unrecognized"
                    + renderTokenList(unrecognizedTokens)
                    + " from value of attribute"
                    + " \u201Crole\u201D. Browsers ignore any"
                    + " token that is not a defined ARIA"
                    + " non-abstract role.", locator));

        }
        if (errorHandler != null && roleValue != null
                && superfluousTokens.size() > 0) {
            errorHandler.error(new SAXParseException("Discarding superfluous"
                    + renderTokenList(superfluousTokens)
                    + " from value of attribute"
                    + " \u201Crole\u201D. Browsers only process"
                    + " the first token found that is a defined"
                    + " ARIA non-abstract role.", locator));

        }
        return roleValue != null ? roleValue : tokenList;
    }
@param ns
@param arg1
@param arg2
@param attributes
@throws SAXException
@see org.xml.sax.ContentHandler#startElement(java.lang.String,
     java.lang.String, java.lang.String, org.xml.sax.Attributes)
src/nu/validator/xml/roleattributes/RoleAttributeFilteringContentHandlerWrapper.java