////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 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();
    }
                                      
}   
