////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.tree.util;

/**
 * ProcInstParser is used to parse pseudo-attributes within Processing Instructions
 *
 * @author Michael H. Kay
 * @version 10 July 2000
 */


public class ProcInstParser {

    /**
     * Class is never instantiated
     */

    private ProcInstParser() {
    }

    /**
     * Get a pseudo-attribute. This is useful only if the processing instruction data part
     * uses pseudo-attribute syntax, which it does not have to. This syntax is as described
     * in the W3C Recommendation "Associating Style Sheets with XML Documents".
     *
     * @return the value of the pseudo-attribute if present, or null if not
     */

    /*@Nullable*/
    public static String getPseudoAttribute(/*@NotNull*/ String content, /*@NotNull*/ String name) {

        int pos = 0;
        while (pos <= content.length() - 4) {     // minimum length of x=""
            int nextQuote = -1;
            for (int q = pos; q < content.length(); q++) {
                if (content.charAt(q) == '"' || content.charAt(q) == '\'') {
                    nextQuote = q;
                    break;
                }
            }
            if (nextQuote < 0) return null;

            int closingQuote = content.indexOf(content.charAt(nextQuote), nextQuote + 1);
            if (closingQuote < 0) return null;
            int nextName = content.indexOf(name, pos);
            if (nextName < 0) return null;
            if (nextName < nextQuote) {
                // check only spaces and equal signs between the name and the quote
                boolean found = true;
                for (int s = nextName + name.length(); s < nextQuote; s++) {
                    char c = content.charAt(s);
                    if (!Character.isWhitespace(c) && c != '=') {
                        found = false;
                        break;
                    }
                }
                if (found) {
                    String val = content.substring(nextQuote + 1, closingQuote);
                    String u = unescape(val);
                    if (u == null) {
                        // unescaping failed
                        return val;
                    } else {
                        return u;
                    }
                }
            }
            pos = closingQuote + 1;
        }
        return null;
    }

    /**
     * Interpret character references and built-in entity references.
     *
     * @return value with character references and built-in entity references expanded.
     *         If badly-formed or unrecognized references are found, return null
     */

    /*@Nullable*/
    private static String unescape(/*@NotNull*/ String value) {
        if (value.indexOf('&') < 0) return value;
        FastStringBuffer sb = new FastStringBuffer(value.length());
        for (int i = 0; i < value.length(); i++) {
            char c = value.charAt(i);
            if (c == '&') {
                if (i + 2 < value.length() && value.charAt(i + 1) == '#') {
                    if (value.charAt(i + 2) == 'x') {
                        int x = i + 3;
                        int charval = 0;
                        while (x < value.length() && value.charAt(x) != ';') {
                            int digit = "0123456789abcdef".indexOf(value.charAt(x));
                            if (digit < 0) {
                                digit = "0123456789ABCDEF".indexOf(value.charAt(x));
                            }
                            if (digit < 0) {
                                return null;
                            }
                            charval = charval * 16 + digit;
                            x++;
                        }
                        char hexchar = (char) charval;
                        sb.append(hexchar);
                        i = x;
                    } else {
                        int x = i + 2;
                        int charval = 0;
                        while (x < value.length() && value.charAt(x) != ';') {
                            int digit = "0123456789".indexOf(value.charAt(x));
                            if (digit < 0) {
                                return null;
                            }
                            charval = charval * 10 + digit;
                            x++;
                        }
                        char decchar = (char) charval;
                        sb.append(decchar);
                        i = x;
                    }
                } else if (value.substring(i + 1).startsWith("lt;")) {
                    sb.append('<');
                    i += 3;
                } else if (value.substring(i + 1).startsWith("gt;")) {
                    sb.append('>');
                    i += 3;
                } else if (value.substring(i + 1).startsWith("amp;")) {
                    sb.append('&');
                    i += 4;
                } else if (value.substring(i + 1).startsWith("quot;")) {
                    sb.append('"');
                    i += 5;
                } else if (value.substring(i + 1).startsWith("apos;")) {
                    sb.append('\'');
                    i += 5;
                } else {
                    return null;
                }

            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

}   
