001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - Bogdan Stefanescu <bs@nuxeo.com> - Constraint API skeleton.
018 *     Nicolas Chapurlat <nchapurlat@nuxeo.com>
019 */
020
021package org.nuxeo.ecm.core.schema.types.constraints;
022
023import java.io.Serializable;
024import java.util.Collections;
025import java.util.Locale;
026import java.util.Map;
027
028/**
029 * A constraint object defines a constraint on a custom type. Method {@link #getDescription()} allows anyone to
030 * dynamically redefine this constraint in another language (for example in javascript to make client-side validation).
031 *
032 * @since 7.1
033 */
034public interface Constraint extends Serializable {
035
036    public static final String MESSAGES_BUNDLE = "messages";
037
038    public static final Locale MESSAGES_DEFAULT_LANG = Locale.ENGLISH;
039
040    public static final String MESSAGES_KEY = "label.schema.constraint.violation";
041
042    /**
043     * Validates the given object against this constraint.
044     * <p>
045     * If some object is null. Constraint should return true while validating unless the constraint deals with nullable
046     * state.
047     * </p>
048     *
049     * @param object the object to validate
050     * @return true if the object was successfully validated, false otherwise
051     */
052    boolean validate(Object object);
053
054    /**
055     * Provides an error message to display when some invalid value does not match existing entity.
056     *
057     * @param invalidValue The invalid value that don't match any entity.
058     * @param locale The language in which the message should be generated.
059     * @return A message in the specified language or
060     * @since 7.1
061     */
062    String getErrorMessage(Object invalidValue, Locale locale);
063
064    /**
065     * Provides a description of a constraint. For example, a constraint which control String format could return
066     * {@code name=PatternMatchingConstraint
067     * | parameters= "pattern":"[0-9]+"}
068     *
069     * @return The constraint description.
070     * @since 7.1
071     */
072    Description getDescription();
073
074    /**
075     * Represent the description of a constraint.
076     * <p>
077     * In the map, key are parameter names, value are parameter values.
078     * </p>
079     *
080     * @since 7.1
081     */
082    class Description {
083
084        private String name;
085
086        private Map<String, Serializable> parameters;
087
088        public Description(String name, Map<String, Serializable> parameters) {
089            super();
090            this.name = name;
091            this.parameters = parameters;
092        }
093
094        public String getName() {
095            return name;
096        }
097
098        public Map<String, Serializable> getParameters() {
099            return Collections.unmodifiableMap(parameters);
100        }
101
102        @Override
103        public int hashCode() {
104            final int prime = 31;
105            int result = 1;
106            result = prime * result + ((name == null) ? 0 : name.hashCode());
107            result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
108            return result;
109        }
110
111        @Override
112        public boolean equals(Object obj) {
113            if (this == obj) {
114                return true;
115            }
116            if (obj == null) {
117                return false;
118            }
119            if (getClass() != obj.getClass()) {
120                return false;
121            }
122            Description other = (Description) obj;
123            if (name == null) {
124                if (other.name != null) {
125                    return false;
126                }
127            } else if (!name.equals(other.name)) {
128                return false;
129            }
130            if (parameters == null) {
131                if (other.parameters != null) {
132                    return false;
133                }
134            } else if (!parameters.equals(other.parameters)) {
135                return false;
136            }
137            return true;
138        }
139
140        @Override
141        public String toString() {
142            StringBuilder builder = new StringBuilder(name);
143            builder.append(parameters.toString());
144            return builder.toString();
145        }
146    }
147
148}