public TypedInputSource buildTypedInputSource(String baseUri,
            String publicId, String contentType)
            throws SAXException, SAXParseException {
        TypedInputSource is;
        String type;
        is = new TypedInputSource();
        is.setPublicId(publicId);
        is.setSystemId(baseUri);
        if (contentType != null) {
            String[] params = contentType.split(";");
            if (params.length < 1) {
                malformedContentTypeError(contentType, "");
                return is;
            } else {
                type = params[0].trim().toLowerCase();
            }
            boolean wasRnc = false;
            boolean wasCss = false;
            boolean wasHtml = false;
            if (isAllowRnc()) {
                if (rncContentType(type, is)) {
                    wasRnc = true;
                    is.setType("application/relax-ng-compact-syntax");
                }
            }
            if (isAllowCss()) {
                if ("text/css".equals(type)) {
                    wasCss = true;
                    is.setType("text/css");
                }
            }
            if (!wasRnc && !wasCss) {
                if (isAllowHtml()) {
                    if ("text/html".equals(type) || "text/html-sandboxed".equals(type)) {
                        is.setType(type);
                        wasHtml = true;
                    } else if (isOnlyHtmlAllowed()) {
                        if (laxContentType && "text/plain".equals(type)) {
                            is.setType(type);
                            wasHtml = true;
                            if (errorHandler != null) {
                                errorHandler.warning(new SAXParseException(
                                        "Being lax about non-HTML Content-Type: "
                                                + type, is.getPublicId(),
                                        is.getSystemId(), -1, -1));
                            }
                        } else if ("application/octet-stream".equals(type)) {
                            is.setType(type);
                            wasHtml = true;                            
                        } else {
                            String msg = "Non-HTML Content-Type: \u201C" + type
                                    + "\u201D.";
                            SAXParseException spe = new SAXParseException(msg,
                                    publicId, baseUri, -1, -1,
                                    new SystemIdIOException(baseUri, msg));
                            if (errorHandler != null) {
                                errorHandler.fatalError(spe);
                            }
                            throw spe;
                        }
                    }
                }
                if (!wasHtml
                        && (isAllowGenericXml() || isAllowXhtml() || isAcceptAllKnownXmlTypes())) {
                    if (!xmlContentType(type, is)) {
                        String msg = "Non-XML Content-Type: \u201C" + type
                                + "\u201D.";
                        SAXParseException spe = new SAXParseException(msg,
                                publicId, baseUri, -1, -1,
                                new SystemIdIOException(baseUri, msg));
                        if (errorHandler != null) {
                            errorHandler.fatalError(spe);
                        }
                        throw new NonXmlContentTypeException(
                                String.format("%s: %s", baseUri, msg));
                    } else {
                        is.setType(type);
                    }
                }
            }
            String charset = null;
            char c;
            boolean quoted = false;
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i < params.length; i++) {
                String param = params[i];
                int offset;
                beforeCharset: for (offset = 0; offset < param.length(); offset++) {
                    c = param.charAt(offset);
                    switch (c) {
                        case ' ':
                        case '\t':
                        case '\n':
                        case '\u000C':
                        case '\r':
                            continue;
                        case 'c':
                        case 'C':
                            break beforeCharset;
                        default:
                    }
                }
                inCharset: if (hasCharset(param, offset)) {
                    if (param.length() == 8) {
                        malformedContentTypeError(contentType,
                                "Expected an \u201c=\u201d sign but"
                                        + " \u201ccharset\u201d parameter"
                                        + " ended.");
                        break inCharset;
                    }
                    offset += 7;
                    c = param.charAt(offset);
                    switch (c) {
                        case '=':
                            offset++;
                            break;
                        case ' ':
                        case '\t':
                        case '\n':
                        case '\u000C':
                        case '\r':
                            malformedContentTypeError(contentType,
                                "Whitespace is not allowed before the \u201c=\u201d sign in the \u201ccharset\u201d parameter.");
                            break inCharset;
                        default:
                            malformedContentTypeError(contentType,
                                "Expected an \u201c=\u201d sign but saw \u201c" + c + "\u201d instead.");
                            break inCharset;
                    }
                    if (offset == param.length()) {
                        malformedContentTypeError(contentType,
                                "The empty string is not a valid encoding name.");
                        break inCharset;
                    }
                    c = param.charAt(offset);
                    switch (c) {
                        case '"':
                            offset++;
                            quoted = true;
                            break;
                        case ' ':
                        case '\t':
                        case '\n':
                        case '\u000C':
                        case '\r':
                            malformedContentTypeError(contentType,
                                "Whitespace is not allowed after the \u201c=\u201d sign in the parameter value.");
                            break inCharset;
                        default:
                            break;
                    }
                    inEncodingName: for (int j = offset; j < param.length(); j++) {
                        c = param.charAt(j);
                        switch (c) {
                            case '"':
                                if (!quoted) {
                                    malformedContentTypeError(contentType,
                                        "Unmatched \u201c\"\u201d character in \u201ccharset\u201d parameter.");
                                    break inCharset;
                                }
                                break inEncodingName;
                            case ' ':
                            case '\t':
                            case '\n':
                            case '\u000C':
                            case '\r':
                                break inEncodingName;
                            default:
                        }
                        if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
                                || (c >= 'a' && c <= 'z') || c == '-' || c == '!'
                                || c == '#' || c == '$' || c == '%' || c == '&'
                                || c == '\'' || c == '+' || c == '_' || c == '`'
                                || c == '{' || c == '}' || c == '~' || c == '^')) {
                            malformedContentTypeError(contentType,
                                "The character \u201c" + c + "\u201d is not a valid character in an encoding name.");
                            break inCharset;
                        }
                        offset++;
                        sb.append(c);
                    }
                    if (quoted) {
                        if (param.length() > offset && '"' == param.charAt(offset)) {
                            offset++;
                        } else {
                            malformedContentTypeError(contentType,
                                "Unmatched \u201c\"\u201d character in \u201ccharset\u201d parameter.");
                            break inCharset;
                        }
                    }
                    if (param.length() > offset) {
                        for (int k = offset + 1; k < param.length(); k++) {
                            c = param.charAt(k);
                            switch (c) {
                                case ' ':
                                case '\t':
                                case '\n':
                                case '\u000C':
                                case '\r':
                                    offset++;
                                    continue;
                                default:
                                    malformedContentTypeError(contentType,
                                        "Only whitespace is allowed after the encoding name in the \u201ccharset\u201d parameter. "
                                        + "Found \u201c" + c + "\u201d instead.");
                                    break inCharset;
                            }
                        }
                    }
                    if (sb.length() == 0) {
                        malformedContentTypeError(contentType,
                            "The empty string is not a valid encoding name.");
                    }
                }
                if (sb.length() > 0) {
                    if ('\'' == sb.charAt(0) && '\'' == sb.charAt(sb.length() - 1)) {
                        malformedContentTypeError(contentType,
                            "Single-quoted encoding names are not allowed in the \u201ccharset\u201d parameter.");
                    } else {
                        charset = sb.toString();
                    }
                }
            }
            if (charset != null) {
                is.setEncoding(charset);
            } else if (type.startsWith("text/") && !wasHtml && !wasCss) {
                if (laxContentType) {
                    if (errorHandler != null) {
                        errorHandler.warning(new SAXParseException(
                                "text/* type without a charset parameter seen. Would have defaulted to US-ASCII had the lax option not been chosen.",
                                is.getPublicId(), is.getSystemId(), -1, -1));
                    }
                } else {
                    is.setEncoding("US-ASCII");
                    if (errorHandler != null) {
                        errorHandler.warning(new SAXParseException(
                                "text/* type without a charset parameter seen. Defaulting to US-ASCII per section 3.1 of RFC 3023.",
                                is.getPublicId(), is.getSystemId(), -1, -1));
                    }
                }
            }
        }
        return is;
    }
@param baseUri
@param publicId
@param contentType
@return
@throws SAXException
@throws SAXParseException
src/nu/validator/xml/ContentTypeParser.java