001/* 002 * (C) Copyright 2014 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 * 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.lang.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 * Configure this resolver. 045 * 046 * @param parameters A map of parameter whose keys are parameter names and map value are corresponding values. 047 * @throws IllegalArgumentException If some parameter are not compatible with this resolver. 048 * @throws IllegalStateException If this resolver is already configured. 049 * @since 7.1 050 */ 051 void configure(Map<String, String> parameters) throws IllegalArgumentException, IllegalArgumentException; 052 053 /** 054 * Returns the resolved object types. 055 * 056 * @since 7.2 057 */ 058 List<Class<?>> getManagedClasses(); 059 060 /** 061 * Provides this resolver name. 062 * 063 * @return The resolver name. 064 * @since 7.1 065 */ 066 String getName(); 067 068 /** 069 * Provides this resolver parameters. 070 * 071 * @return A map containing <parameter name , parameter value> 072 * @since 7.1 073 */ 074 Map<String, Serializable> getParameters(); 075 076 /** 077 * Validates some value references an existing entity. 078 * 079 * @param value The reference. 080 * @return true if value could be resolved as an existing external reference, false otherwise. 081 * @throws IllegalStateException If this resolver has not been configured. 082 * @since 7.1 083 */ 084 boolean validate(Object value); 085 086 /** 087 * Provides the entity referenced by a value. 088 * 089 * @param value The reference. 090 * @return The referenced entity, null if no entity matches the value. 091 * @throws IllegalStateException If this resolver has not been configured. 092 * @since 7.1 093 */ 094 Object fetch(Object value); 095 096 /** 097 * Provides the entity referenced by a value, return the entity as expected type. 098 * 099 * @param value The reference. 100 * @return The referenced entity, null if no entity matches the value or if this entity cannot be converted as type. 101 * @throws IllegalStateException If this resolver has not been configured. 102 * @since 7.1 103 */ 104 <T> T fetch(Class<T> type, Object value); 105 106 /** 107 * Generates a reference to an entity. 108 * 109 * @param object The entity. 110 * @return A reference to the entity or null if its not a managed entity type. 111 * @throws IllegalStateException If this resolver has not been configured. 112 * @since 7.1 113 */ 114 Serializable getReference(Object object); 115 116 /** 117 * Provides an error message to display when some invalid value does not match existing entity. 118 * 119 * @param invalidValue The invalid value that don't match any entity. 120 * @param locale The language in which the message should be generated. 121 * @return A message in the specified language or 122 * @since 7.1 123 */ 124 String getConstraintErrorMessage(Object invalidValue, Locale locale); 125 126 /** 127 * Manage translation for resolver : {@link #getConstraintErrorMessage(ObjectResolver, Object, Locale)} 128 * 129 * @since 7.1 130 */ 131 public static final class Helper { 132 133 private static final Log log = LogFactory.getLog(Helper.class); 134 135 private Helper() { 136 } 137 138 /** 139 * Use a default translation key : label.schema.constraint.resolver.[Resolver.getName()] 140 * 141 * @param resolver The requesting resolver. 142 * @param suffixCase This field is a which allow to define alternative translation. 143 * @param invalidValue The invalid value that don't match any entity. 144 * @param locale The language in which the message should be generated. 145 * @param additionnalParameters Relayed elements to build the message. 146 * @return A message in the specified language 147 * @since 7.1 148 */ 149 public static String getConstraintErrorMessage(ObjectResolver resolver, String suffixCase, Object invalidValue, 150 Locale locale, String... additionnalParameters) { 151 List<String> pathTokens = new ArrayList<String>(); 152 pathTokens.add(Constraint.MESSAGES_KEY); 153 pathTokens.add("resolver"); 154 pathTokens.add(resolver.getName()); 155 if (suffixCase != null) { 156 pathTokens.add(suffixCase); 157 } 158 String keyConstraint = StringUtils.join(pathTokens, '.'); 159 String computedInvalidValue = "null"; 160 if (invalidValue != null) { 161 String invalidValueString = invalidValue.toString(); 162 if (invalidValueString.length() > 20) { 163 computedInvalidValue = invalidValueString.substring(0, 15) + "..."; 164 } else { 165 computedInvalidValue = invalidValueString; 166 } 167 } 168 Object[] params = new Object[1 + additionnalParameters.length]; 169 params[0] = computedInvalidValue; 170 for (int i = 1; i < params.length; i++) { 171 params[i] = additionnalParameters[i - 1]; 172 } 173 Locale computedLocale = locale != null ? locale : Constraint.MESSAGES_DEFAULT_LANG; 174 String message; 175 try { 176 message = I18NUtils.getMessageString(Constraint.MESSAGES_BUNDLE, keyConstraint, params, computedLocale); 177 } catch (MissingResourceException e) { 178 log.trace("No bundle found", e); 179 return null; 180 } 181 if (message != null && !message.trim().isEmpty() && !keyConstraint.equals(message)) { 182 // use a constraint specific message if there's one 183 return message; 184 } else { 185 return String.format("%s cannot resolve reference %s", resolver.getName(), computedInvalidValue); 186 } 187 } 188 189 /** 190 * Use a default translation key : label.schema.constraint.resolver.[Resolver.getName()] 191 * 192 * @param resolver The requesting resolver. 193 * @param invalidValue The invalid value that don't match any entity. 194 * @param locale The language in which the message should be generated. 195 * @param additionnalParameters Relayed elements to build the message. 196 * @return A message in the specified language 197 * @since 7.1 198 */ 199 public static String getConstraintErrorMessage(ObjectResolver resolver, Object invalidValue, Locale locale, 200 String... additionnalParameters) { 201 return Helper.getConstraintErrorMessage(resolver, null, invalidValue, locale, additionnalParameters); 202 } 203 } 204 205}