/*
 * $Id: ObjectRef.java 220 2007-07-16 10:32:15Z sugimotokenichi $
 * Copyright (C) 2006 SUGIMOTO Ken-ichi
 * 作成日: 2006/03/15
 */
package feat2.config.type;

import java.util.regex.Pattern;

import feat2.validation.ValidatorUtil;

/**
 * オブジェクトの参照式。
 * @author SUGIMOTO Ken-ichi
 */
public class ObjectRef {

    private String name;
    private String nestedPropertyName;

    public ObjectRef() {
    }

    public ObjectRef(String ref) throws IllegalArgumentException {

        int dotIdx = ref.indexOf('.');
        if ( dotIdx == -1 ) {

            // プロパティの指定がない場合

            if ( Identifier.isIdentifier(ref) ) {
                this.name = ref;
                this.nestedPropertyName = null;
            }
            else {
                throw new IllegalArgumentException(); // TODO エラーメッセージ
            }

        }
        else {

            // プロパティが指定された参照式

            name = ref.substring(0, dotIdx);
            nestedPropertyName = ref.substring(dotIdx+1);

            // オブジェクト名
            if ( !Identifier.isIdentifier(name) )
                throw new IllegalArgumentException(); // TODO エラーメッセージ

            // プロパティ
            if ( !isNestedPropertyName(nestedPropertyName) )
                throw new IllegalArgumentException(); // TODO エラーメッセージ

        }

    }



    // Objectのメソッド -------------------------------------------------------

    public boolean equals(Object obj) {
        return
            ValidatorUtil.equalOrNull(name, ((ObjectRef)obj).name)
            && ValidatorUtil.equalOrNull(nestedPropertyName, ((ObjectRef)obj).nestedPropertyName);
    }

    public int hashCode() {
        int hash = name.hashCode();

        if ( nestedPropertyName != null )
            hash ^= nestedPropertyName.hashCode();

        return hash;
    }

    public String toString() {
        if ( nestedPropertyName != null )
            return name + '.' + nestedPropertyName;
        else
            return name;
    }


    // プロパティ -------------------------------------------------------------

    public String getNestedPropertyName() {
        return nestedPropertyName;
    }

    public String getName() {
        return name;
    }


    // スタティックメソッド ---------------------------------------------------

    /**
     * 文字列がオブジェクトの参照式ならtrueを返す。
     * @param string
     * @return
     */
    public static boolean isNestedPropertyName(String string) {

        // 文字を含まなかったらfalse

        if ( ValidatorUtil.isBlankOrNull(string) )
            return false;

        // プロパティ名を検証

        String[] tokens = null;
        if ( string.indexOf('.') > -1 ) {
            tokens = string.split("\\.");
        }
        else {
            return isPropertyExpression(string);
        }

        for (int i = 0; i < tokens.length; i++) {
            if ( !isPropertyExpression(tokens[i]) )
                return false;
        }

        return true;

    }

    private static Pattern mappedOptionPattern = Pattern.compile("^\\(.+\\)$");
    private static Pattern indexedOptionPattern = Pattern.compile("^\\[[0-9]+\\]$");

    public static boolean isPropertyExpression(String str) {

        if ( ValidatorUtil.isBlankOrNull(str) )
            return false;

        int mappedIdx = str.indexOf("(");
        int indexedIdx = str.indexOf('[');

        String name = null;

        if ( mappedIdx > -1 ) {
            name = str.substring(0, mappedIdx);
            String opt = str.substring(mappedIdx+1);
            if ( !mappedOptionPattern.matcher(opt).matches() )
                return false;
        }

        else if ( indexedIdx > -1 ) {
            name = str.substring(0, indexedIdx);
            String opt = str.substring(indexedIdx+1);
            if ( !indexedOptionPattern.matcher(opt).matches() )
                return false;
        }

        else {
            name = str;
        }

        return JavaIdentifier.isJavaIdentifier(name);

    }


}
