001/* 002 * (C) Copyright 2014-2018 Nuxeo (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 * Nicolas Chapurlat <nchapurlat@nuxeo.com> 018 */ 019 020package org.nuxeo.ecm.core.schema.types.resolver; 021 022import java.io.Serializable; 023import java.util.ArrayList; 024import java.util.List; 025import java.util.Locale; 026import java.util.Map; 027import java.util.MissingResourceException; 028 029import org.apache.commons.lang3.StringUtils; 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.common.utils.i18n.I18NUtils; 033import org.nuxeo.ecm.core.schema.types.constraints.Constraint; 034 035/** 036 * External references are document field with a simple type whose value refers to an external business entity. Objects 037 * implementing this interface are able to resolve the entity using the reference. 038 * 039 * @since 7.1 040 */ 041public interface ObjectResolver extends Serializable { 042 043 /** 044 * The validation parameter key to enable/disable validation on resolver. Enabled by default. 045 * 046 * @since 10.2 047 */ 048 String VALIDATION_PARAMETER_KEY = "validation"; 049 050 /** 051 * Configure this resolver. 052 * 053 * @param parameters A map of parameter whose keys are parameter names and map value are corresponding values. 054 * @throws IllegalArgumentException If some parameter are not compatible with this resolver. 055 * @throws IllegalStateException If this resolver is already configured. 056 * @since 7.1 057 */ 058 void configure(Map<String, String> parameters) throws IllegalArgumentException, IllegalStateException; 059 060 /** 061 * Returns the resolved object types. 062 * 063 * @since 7.2 064 */ 065 List<Class<?>> getManagedClasses(); 066 067 /** 068 * Provides this resolver name. 069 * 070 * @return The resolver name. 071 * @since 7.1 072 */ 073 String getName(); 074 075 /** 076 * Provides this resolver parameters. 077 * 078 * @return A map containing associating parameter name to its value 079 * @since 7.1 080 */ 081 Map<String, Serializable> getParameters(); 082 083 /** 084 * Validates some value references an existing entity. 085 * 086 * @param value The reference. 087 * @return true if value could be resolved as an existing external reference, false otherwise. 088 * @throws IllegalStateException If this resolver has not been configured. 089 * @since 7.1 090 */ 091 boolean validate(Object value); 092 093 /** 094 * Validates some value references an existing entity, in the given context 095 * 096 * @param value The reference. 097 * @param context A resolver-specific context allowing resolution of the value. 098 * @return true if value could be resolved as an existing external reference, false otherwise. 099 * @throws IllegalStateException If this resolver has not been configured. 100 * @since 10.2 101 */ 102 default boolean validate(Object value, Object context) { 103 return validate(value); 104 } 105 106 /** 107 * Provides the entity referenced by a value. 108 * 109 * @param value The reference. 110 * @return The referenced entity, null if no entity matches the value. 111 * @throws IllegalStateException If this resolver has not been configured. 112 * @since 7.1 113 */ 114 Object fetch(Object value); 115 116 /** 117 * Provides the entity referenced by a value, in the given context. 118 * 119 * @param value The reference. 120 * @param context A resolver-specific context allowing resolution of the value. 121 * @return The referenced entity, null if no entity matches the value. 122 * @throws IllegalStateException If this resolver has not been configured. 123 * @since 10.2 124 */ 125 default Object fetch(Object value, Object context) { 126 return fetch(value); 127 } 128 129 /** 130 * Provides the entity referenced by a value, return the entity as expected type. 131 * 132 * @param value The reference. 133 * @return The referenced entity, null if no entity matches the value or if this entity cannot be converted as type. 134 * @throws IllegalStateException If this resolver has not been configured. 135 * @since 7.1 136 */ 137 <T> T fetch(Class<T> type, Object value); 138 139 /** 140 * Generates a reference to an entity. 141 * 142 * @param object The entity. 143 * @return A reference to the entity or null if its not a managed entity type. 144 * @throws IllegalStateException If this resolver has not been configured. 145 * @since 7.1 146 */ 147 Serializable getReference(Object object); 148 149 /** 150 * Provides an error message to display when some invalid value does not match existing entity. 151 * 152 * @param invalidValue The invalid value that don't match any entity. 153 * @param locale The language in which the message should be generated. 154 * @return A message in the specified language or 155 * @since 7.1 156 */ 157 String getConstraintErrorMessage(Object invalidValue, Locale locale); 158 159 /** 160 * Manage translation for resolver : {@link #getConstraintErrorMessage(ObjectResolver, Object, Locale, String...)} 161 * 162 * @since 7.1 163 */ 164 final class Helper { 165 166 private static final Log log = LogFactory.getLog(Helper.class); 167 168 private Helper() { 169 } 170 171 /** 172 * Use a default translation key : label.schema.constraint.resolver.[Resolver.getName()] 173 * 174 * @param resolver The requesting resolver. 175 * @param suffixCase This field is a which allow to define alternative translation. 176 * @param invalidValue The invalid value that don't match any entity. 177 * @param locale The language in which the message should be generated. 178 * @param additionnalParameters Relayed elements to build the message. 179 * @return A message in the specified language 180 * @since 7.1 181 */ 182 public static String getConstraintErrorMessage(ObjectResolver resolver, String suffixCase, Object invalidValue, 183 Locale locale, String... additionnalParameters) { 184 List<String> pathTokens = new ArrayList<>(); 185 pathTokens.add(Constraint.MESSAGES_KEY); 186 pathTokens.add("resolver"); 187 pathTokens.add(resolver.getName()); 188 if (suffixCase != null) { 189 pathTokens.add(suffixCase); 190 } 191 String keyConstraint = StringUtils.join(pathTokens, '.'); 192 String computedInvalidValue = "null"; 193 if (invalidValue != null) { 194 String invalidValueString = invalidValue.toString(); 195 if (invalidValueString.length() > 20) { 196 computedInvalidValue = invalidValueString.substring(0, 15) + "..."; 197 } else { 198 computedInvalidValue = invalidValueString; 199 } 200 } 201 Object[] params = new Object[1 + additionnalParameters.length]; 202 params[0] = computedInvalidValue; 203 System.arraycopy(additionnalParameters, 0, params, 1, params.length - 1); 204 Locale computedLocale = locale != null ? locale : Constraint.MESSAGES_DEFAULT_LANG; 205 String message; 206 try { 207 message = I18NUtils.getMessageString(Constraint.MESSAGES_BUNDLE, keyConstraint, params, computedLocale); 208 } catch (MissingResourceException e) { 209 log.trace("No bundle found", e); 210 return null; 211 } 212 if (message != null && !message.trim().isEmpty() && !keyConstraint.equals(message)) { 213 // use a constraint specific message if there's one 214 return message; 215 } else { 216 return String.format("%s cannot resolve reference %s", resolver.getName(), computedInvalidValue); 217 } 218 } 219 220 /** 221 * Use a default translation key : label.schema.constraint.resolver.[Resolver.getName()] 222 * 223 * @param resolver The requesting resolver. 224 * @param invalidValue The invalid value that don't match any entity. 225 * @param locale The language in which the message should be generated. 226 * @param additionnalParameters Relayed elements to build the message. 227 * @return A message in the specified language 228 * @since 7.1 229 */ 230 public static String getConstraintErrorMessage(ObjectResolver resolver, Object invalidValue, Locale locale, 231 String... additionnalParameters) { 232 return Helper.getConstraintErrorMessage(resolver, null, invalidValue, locale, additionnalParameters); 233 } 234 } 235 236}