001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nicolas Chapurlat <nchapurlat@nuxeo.com> 016 */ 017 018package org.nuxeo.ecm.core.schema.types.resolver; 019 020import java.io.Serializable; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.Locale; 024import java.util.Map; 025import java.util.MissingResourceException; 026 027import org.apache.commons.lang.StringUtils; 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030import org.nuxeo.common.utils.i18n.I18NUtils; 031import org.nuxeo.ecm.core.schema.types.constraints.Constraint; 032 033/** 034 * External references are document field with a simple type whose value refers to an external business entity. Objects 035 * implementing this interface are able to resolve the entity using the reference. 036 * 037 * @since 7.1 038 */ 039public interface ObjectResolver extends Serializable { 040 041 /** 042 * Configure this resolver. 043 * 044 * @param parameters A map of parameter whose keys are parameter names and map value are corresponding values. 045 * @throws IllegalArgumentException If some parameter are not compatible with this resolver. 046 * @throws IllegalStateException If this resolver is already configured. 047 * @since 7.1 048 */ 049 void configure(Map<String, String> parameters) throws IllegalArgumentException, IllegalArgumentException; 050 051 /** 052 * Returns the resolved object types. 053 * 054 * @since 7.2 055 */ 056 List<Class<?>> getManagedClasses(); 057 058 /** 059 * Provides this resolver name. 060 * 061 * @return The resolver name. 062 * @since 7.1 063 */ 064 String getName(); 065 066 /** 067 * Provides this resolver parameters. 068 * 069 * @return A map containing <parameter name , parameter value> 070 * @since 7.1 071 */ 072 Map<String, Serializable> getParameters(); 073 074 /** 075 * Validates some value references an existing entity. 076 * 077 * @param value The reference. 078 * @return true if value could be resolved as an existing external reference, false otherwise. 079 * @throws IllegalStateException If this resolver has not been configured. 080 * @since 7.1 081 */ 082 boolean validate(Object value); 083 084 /** 085 * Provides the entity referenced by a value. 086 * 087 * @param value The reference. 088 * @return The referenced entity, null if no entity matches the value. 089 * @throws IllegalStateException If this resolver has not been configured. 090 * @since 7.1 091 */ 092 Object fetch(Object value); 093 094 /** 095 * Provides the entity referenced by a value, return the entity as expected type. 096 * 097 * @param value The reference. 098 * @return The referenced entity, null if no entity matches the value or if this entity cannot be converted as type. 099 * @throws IllegalStateException If this resolver has not been configured. 100 * @since 7.1 101 */ 102 <T> T fetch(Class<T> type, Object value); 103 104 /** 105 * Generates a reference to an entity. 106 * 107 * @param object The entity. 108 * @return A reference to the entity or null if its not a managed entity type. 109 * @throws IllegalStateException If this resolver has not been configured. 110 * @since 7.1 111 */ 112 Serializable getReference(Object object); 113 114 /** 115 * Provides an error message to display when some invalid value does not match existing entity. 116 * 117 * @param invalidValue The invalid value that don't match any entity. 118 * @param locale The language in which the message should be generated. 119 * @return A message in the specified language or 120 * @since 7.1 121 */ 122 String getConstraintErrorMessage(Object invalidValue, Locale locale); 123 124 /** 125 * Manage translation for resolver : {@link #getConstraintErrorMessage(ObjectResolver, Object, Locale)} 126 * 127 * @since 7.1 128 */ 129 public static final class Helper { 130 131 private static final Log log = LogFactory.getLog(Helper.class); 132 133 private Helper() { 134 } 135 136 /** 137 * Use a default translation key : label.schema.constraint.resolver.[Resolver.getName()] 138 * 139 * @param resolver The requesting resolver. 140 * @param suffixCase This field is a which allow to define alternative translation. 141 * @param invalidValue The invalid value that don't match any entity. 142 * @param locale The language in which the message should be generated. 143 * @param additionnalParameters Relayed elements to build the message. 144 * @return A message in the specified language 145 * @since 7.1 146 */ 147 public static String getConstraintErrorMessage(ObjectResolver resolver, String suffixCase, Object invalidValue, 148 Locale locale, String... additionnalParameters) { 149 List<String> pathTokens = new ArrayList<String>(); 150 pathTokens.add(Constraint.MESSAGES_KEY); 151 pathTokens.add("resolver"); 152 pathTokens.add(resolver.getName()); 153 if (suffixCase != null) { 154 pathTokens.add(suffixCase); 155 } 156 String keyConstraint = StringUtils.join(pathTokens, '.'); 157 String computedInvalidValue = "null"; 158 if (invalidValue != null) { 159 String invalidValueString = invalidValue.toString(); 160 if (invalidValueString.length() > 20) { 161 computedInvalidValue = invalidValueString.substring(0, 15) + "..."; 162 } else { 163 computedInvalidValue = invalidValueString; 164 } 165 } 166 Object[] params = new Object[1 + additionnalParameters.length]; 167 params[0] = computedInvalidValue; 168 for (int i = 1; i < params.length; i++) { 169 params[i] = additionnalParameters[i - 1]; 170 } 171 Locale computedLocale = locale != null ? locale : Constraint.MESSAGES_DEFAULT_LANG; 172 String message; 173 try { 174 message = I18NUtils.getMessageString(Constraint.MESSAGES_BUNDLE, keyConstraint, params, computedLocale); 175 } catch (MissingResourceException e) { 176 log.trace("No bundle found", e); 177 return null; 178 } 179 if (message != null && !message.trim().isEmpty() && !keyConstraint.equals(message)) { 180 // use a constraint specific message if there's one 181 return message; 182 } else { 183 return String.format("%s cannot resolve reference %s", resolver.getName(), computedInvalidValue); 184 } 185 } 186 187 /** 188 * Use a default translation key : label.schema.constraint.resolver.[Resolver.getName()] 189 * 190 * @param resolver The requesting resolver. 191 * @param invalidValue The invalid value that don't match any entity. 192 * @param locale The language in which the message should be generated. 193 * @param additionnalParameters Relayed elements to build the message. 194 * @return A message in the specified language 195 * @since 7.1 196 */ 197 public static String getConstraintErrorMessage(ObjectResolver resolver, Object invalidValue, Locale locale, 198 String... additionnalParameters) { 199 return Helper.getConstraintErrorMessage(resolver, null, invalidValue, locale, additionnalParameters); 200 } 201 } 202 203}