/*
 * Copyright (c) 2001-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.sun.msv.datatype.xsd;

import com.sun.msv.datatype.xsd.regex.RegExp;
import com.sun.msv.datatype.xsd.regex.RegExpFactory;
import org.relaxng.datatype.DatatypeException;
import org.relaxng.datatype.ValidationContext;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Vector;
import java.text.ParseException;

/**
 * "pattern" facet validator
 * 
 * "pattern" is a constraint facet which is applied against lexical space.
 * See http://www.w3.org/TR/xmlschema-2/#dt-pattern for the spec
 * 
 * @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a>
 */
public final class PatternFacet extends DataTypeWithLexicalConstraintFacet {
    
    /**
     * actual object that performs regular expression validation.
     * one of the item has to match.
     *
     * Don't serialize so that we won't have hard-coded dependency to
     * a particular regexp lib.
     */
    private transient RegExp[] exps;
    
    public RegExp[] getRegExps() { return exps; }
    
    /**
     * string representations of the above RegularExpressions.
     * this representation is usually human friendly than
     * the one generated by RegularExpression.toString method.
     */
    final public String[] patterns;

    
    
    public PatternFacet( String nsUri, String typeName, XSDatatypeImpl baseType, TypeIncubator facets )
        throws DatatypeException {
        super( nsUri, typeName, baseType, FACET_PATTERN, facets.isFixed(FACET_PATTERN) );
        
        
        // TODO : am I supposed to implement my own regexp validator?
        // at this time, I use Xerces' one.
        
        Vector regExps = facets.getVector(FACET_PATTERN);
        patterns = (String[]) regExps.toArray(new String[regExps.size()]);
        
        try {
            compileRegExps();
        } catch( ParseException pe ) {
            // in case regularExpression is not a correct pattern
            throw new DatatypeException( localize( ERR_PARSE_ERROR,
                pe.getMessage() ) );
        }
    }
    
    /** Compiles all the regular expressions. */
    private void compileRegExps() throws ParseException {
        exps = new RegExp[patterns.length];
        RegExpFactory factory = RegExpFactory.createFactory();
        for(int i=0;i<exps.length;i++)
            exps[i] = factory.compile(patterns[i]);
        
        // loosened facet check is almost impossible for pattern facet.
        // ignore it for now.
    }
    
    protected void diagnoseByFacet(String content, ValidationContext context) throws DatatypeException {
        if( checkLexicalConstraint(content) )    return;
        
        if( exps.length==1 )
            throw new DatatypeException( DatatypeException.UNKNOWN,
                localize(ERR_PATTERN_1,patterns[0]) );
        else
            throw new DatatypeException( DatatypeException.UNKNOWN,
                localize(ERR_PATTERN_MANY) );
    }
    
    protected final boolean checkLexicalConstraint( String literal ) {
        // makes sure that at least one of the patterns is satisfied.
        
        // regexp can be not thread-safe. Make sure only one thread uses it
        // at any given time.
        synchronized(this) {
            for( int i=0; i<exps.length; i++ )
                if(exps[i].matches(literal))
                    return true;
        }
        // otherwise fail
        return false;
    }
    
    
    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
        stream.defaultReadObject();

        try {
            compileRegExps();
        } catch (ParseException e) {
            throw new IOException(e.getMessage());
        }
    }
    

    // serialization support
    private static final long serialVersionUID = 1;
}
