/* UtilLib - Copyright (c) 2018 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.attr;

import java.text.ParseException;


/**
 * [_a-zA-Z][-_.:/a-zA-Z0-9]*
 *
 * @author Vincent Calame
 */
public class CheckedLocalKey implements CharSequence {

    private final String s;

    private CheckedLocalKey(String s) {
        this.s = s;
    }

    @Override
    public int length() {
        return s.length();
    }

    @Override
    public char charAt(int index) {
        return s.charAt(index);
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        return s.subSequence(start, end);
    }

    @Override
    public String toString() {
        return s;
    }

    public static CheckedLocalKey parse(CharSequence charSequence) throws ParseException {
        int length = charSequence.length();
        if (length == 0) {
            throw new ParseException("Empty charSequence", 0);
        }
        if (!testFirstChar(charSequence.charAt(0))) {
            throw new ParseException("Wrong first char (" + charSequence.charAt(0) + ")", 0);
        }
        for (int i = 1; i < length; i++) {
            char carac = charSequence.charAt(i);
            if (!testChar(carac)) {
                throw new ParseException("Wrong char (" + carac + ")", i);
            }
        }
        return new CheckedLocalKey(charSequence.toString());
    }

    /**
     * Équivalent de parse() mais en envoyant IllegalArgumentException plutôt
     * que ParseException. à utiliser quand on est sûr de la syntaxe et permet
     * d'éviter un try {} catch {}
     */
    public static CheckedLocalKey build(CharSequence charSequence) {
        try {
            return parse(charSequence);
        } catch (ParseException pe) {
            throw new IllegalArgumentException(pe.getMessage());
        }
    }

    private static boolean testFirstChar(char carac) {
        if (carac == '_') {
            return true;
        }
        if ((carac >= 'a') && (carac <= 'z')) {
            return true;
        }
        if ((carac >= 'A') && (carac <= 'Z')) {
            return true;
        }
        return false;
    }

    private static boolean testChar(char carac) {
        if (carac == '_') {
            return true;
        }
        if ((carac >= 'a') && (carac <= 'z')) {
            return true;
        }
        if ((carac >= 'A') && (carac <= 'Z')) {
            return true;
        }
        if ((carac >= '0') && (carac <= '9')) {
            return true;
        }
        switch (carac) {
            case '_':
            case '-':
            case '.':
            case ':':
            case '/':
                return true;
            default:
                return false;
        }
    }

}
