001// Generated by delombok at Sun Jul 30 17:21:56 UTC 2023 002package de.cuioss.test.jsf.config.decorator; 003 004import static de.cuioss.tools.base.Preconditions.checkArgument; 005import static java.util.Objects.requireNonNull; 006import javax.faces.application.Application; 007import javax.faces.component.FacesComponent; 008import javax.faces.component.UIComponent; 009import javax.faces.component.UIForm; 010import javax.faces.component.UIInput; 011import javax.faces.component.UIOutput; 012import javax.faces.component.UISelectBoolean; 013import javax.faces.component.UISelectOne; 014import javax.faces.component.UIViewRoot; 015import javax.faces.component.behavior.ClientBehavior; 016import javax.faces.component.behavior.FacesBehavior; 017import javax.faces.component.html.HtmlCommandButton; 018import javax.faces.component.html.HtmlForm; 019import javax.faces.component.html.HtmlInputText; 020import javax.faces.component.html.HtmlOutputText; 021import javax.faces.component.html.HtmlSelectBooleanCheckbox; 022import javax.faces.component.html.HtmlSelectOneRadio; 023import javax.faces.context.FacesContext; 024import javax.faces.convert.Converter; 025import javax.faces.convert.FacesConverter; 026import javax.faces.render.FacesRenderer; 027import javax.faces.render.Renderer; 028import javax.faces.validator.FacesValidator; 029import javax.faces.validator.Validator; 030import de.cuioss.test.jsf.mocks.CuiMockComponent; 031import de.cuioss.test.jsf.mocks.CuiMockRenderer; 032import de.cuioss.test.jsf.mocks.CuiMockUIViewRoot; 033import de.cuioss.test.jsf.mocks.CuiMockViewHandler; 034import de.cuioss.test.valueobjects.objects.impl.DefaultInstantiator; 035import lombok.NonNull; 036 037/** 038 * Helper class acting as runtime-registry for {@link UIComponent}, 039 * {@link Converter}, {@link Validator} and {@link Renderer} 040 * 041 * @author Oliver Wolff 042 */ 043public class ComponentConfigDecorator { 044 static final String FORM_RENDERER_ID = "javax.faces.Form"; 045 static final String TEXT_RENDERER_ID = "javax.faces.Text"; 046 static final String SELECTBOOLEAN_RENDERER_ID = "javax.faces.SelectBoolean"; 047 static final String SELECTONE_RENDERER_ID = "javax.faces.SelectOne"; 048 private static final String BEHAVIOR_CLASS_MUST_NOT_BE_NULL = "behaviorClass must not be null"; 049 private static final String BEHAVIOR_ID_MUST_NOT_BE_NULL = "behaviorId must not be null"; 050 private static final String RENDERER_MUST_NOT_BE_NULL = "renderer must not be null"; 051 private static final String RENDERER_TYPE_MUST_NOT_BE_NULL = "rendererType must not be null"; 052 private static final String FAMILY_MUST_NOT_BE_NULL = "family must not be null"; 053 private static final String VALIDATOR_ID_MUST_NOT_BE_NULL = "validatorId must not be null"; 054 private static final String CONVERTER_ID_MUST_NOT_BE_NULL = "converterId must not be null"; 055 private static final String TARGET_CLASS_MUST_NOT_BE_NULL = "targetClass must not be null"; 056 private static final String VALIDATOR_MUST_NOT_BE_NULL = "validator must not be null"; 057 private static final String COMPONENT_MUST_NOT_BE_NULL = "component must not be null"; 058 private static final String COMPONENT_TYPE_NOT_BE_NULL = "component must not be null"; 059 private static final String CONVERTER_MUST_NOT_BE_NULL = "converter must not be null"; 060 @NonNull 061 private final Application application; 062 @NonNull 063 private final FacesContext facesContext; 064 065 /** 066 * Adds add {@link Validator} to the given id. 067 * 068 * @param validatorId the id the {@link Validator} should be registered with, 069 * must not be null 070 * @param validator the actual {@link Validator} class, must not be null. 071 * @return the {@link ComponentConfigDecorator} itself in order to enable a 072 * fluent-api style usage 073 */ 074 public ComponentConfigDecorator registerValidator(final String validatorId, final Class<? extends Validator> validator) { 075 requireNonNull(validator, VALIDATOR_MUST_NOT_BE_NULL); 076 requireNonNull(validatorId, VALIDATOR_ID_MUST_NOT_BE_NULL); 077 application.addValidator(validatorId, validator.getName()); 078 return this; 079 } 080 081 /** 082 * Adds add {@link Validator} the needed validatorId is retrieved from 083 * {@link FacesValidator}. 084 * 085 * @param validator the actual {@link Validator} class, must not be null. In 086 * order to work the {@link Validator} must provide the 087 * {@link FacesValidator} annotation in order to identify the 088 * the needed validatorId 089 * @return the {@link ComponentConfigDecorator} itself in order to enable a 090 * fluent-api style usage 091 */ 092 public ComponentConfigDecorator registerValidator(final Class<? extends Validator> validator) { 093 requireNonNull(validator, VALIDATOR_MUST_NOT_BE_NULL); 094 checkArgument(validator.isAnnotationPresent(FacesValidator.class), "In order to work this method needs a validator annotated with \'javax.faces.validator.FacesValidator\', validatorClass:" + validator.getName()); 095 return registerValidator(validator.getAnnotation(FacesValidator.class).value(), validator); 096 } 097 098 /** 099 * Register a {@link Converter} for a given target class. 100 * 101 * @param converter to be registered , must not be null 102 * @param targetClass must not be null 103 * @return the {@link ComponentConfigDecorator} itself in order to enable a 104 * fluent-api style usage 105 */ 106 public ComponentConfigDecorator registerConverter(final Class<? extends Converter> converter, final Class<?> targetClass) { 107 requireNonNull(targetClass, TARGET_CLASS_MUST_NOT_BE_NULL); 108 requireNonNull(converter, CONVERTER_MUST_NOT_BE_NULL); 109 application.addConverter(targetClass, converter.getName()); 110 return this; 111 } 112 113 /** 114 * Register a {@link Converter} for a given target class. 115 * 116 * @param converter to be registered , must not be null and must provide a 117 * {@link FacesConverter} annotation for deriving targetType or 118 * converter.id 119 * @return the {@link ComponentConfigDecorator} itself in order to enable a 120 * fluent-api style usage 121 */ 122 public ComponentConfigDecorator registerConverter(final Class<? extends Converter> converter) { 123 requireNonNull(converter, CONVERTER_MUST_NOT_BE_NULL); 124 checkArgument(converter.isAnnotationPresent(FacesConverter.class), "In order to work this method needs a converter annotated with \'javax.faces.convert.FacesConverter\', converterClass:" + converter.getName()); 125 final var facesConverter = converter.getAnnotation(FacesConverter.class); 126 if (!Object.class.equals(facesConverter.forClass())) { 127 registerConverter(converter, facesConverter.forClass()); 128 } 129 if (!facesConverter.value().isEmpty()) { 130 registerConverter(converter, facesConverter.value()); 131 } 132 return this; 133 } 134 135 /** 136 * Register a {@link Converter} to a given converterId. 137 * 138 * @param converter to be registered , must not be null 139 * @param converterId must not be null 140 * @return the {@link ComponentConfigDecorator} itself in order to enable a 141 * fluent-api style usage 142 */ 143 public ComponentConfigDecorator registerConverter(final Class<? extends Converter> converter, final String converterId) { 144 requireNonNull(converterId, CONVERTER_ID_MUST_NOT_BE_NULL); 145 requireNonNull(converter, CONVERTER_MUST_NOT_BE_NULL); 146 application.addConverter(converterId, converter.getName()); 147 return this; 148 } 149 150 /** 151 * Registers a {@link UIComponent} 152 * 153 * @param componentType identifying the component, must not be null 154 * @param component the actual component, must not be null 155 * @return the {@link ComponentConfigDecorator} itself in order to enable a 156 * fluent-api style usage 157 */ 158 public ComponentConfigDecorator registerUIComponent(final String componentType, final Class<? extends UIComponent> component) { 159 requireNonNull(componentType, COMPONENT_TYPE_NOT_BE_NULL); 160 requireNonNull(component, COMPONENT_MUST_NOT_BE_NULL); 161 application.addComponent(componentType, component.getName()); 162 return this; 163 } 164 165 /** 166 * Registers a {@link UIComponent} 167 * 168 * @param component the actual component, must not be null must provide the 169 * {@link FacesComponent} annotation to derive the componentId 170 * to be registered to. 171 * @return the {@link ComponentConfigDecorator} itself in order to enable a 172 * fluent-api style usage 173 */ 174 public ComponentConfigDecorator registerUIComponent(final Class<? extends UIComponent> component) { 175 requireNonNull(component, COMPONENT_MUST_NOT_BE_NULL); 176 checkArgument(component.isAnnotationPresent(FacesComponent.class), "In order to work this method needs a UIComponent annotated with \'javax.faces.component.FacesComponent\', component:" + component.getName()); 177 return registerUIComponent(component.getAnnotation(FacesComponent.class).value(), component); 178 } 179 180 /** 181 * Shorthand for registering a {@link CuiMockComponent} with a 182 * {@link CuiMockRenderer}. 183 * 184 * @return the {@link ComponentConfigDecorator} itself in order to enable a 185 * fluent-api style usage 186 */ 187 public ComponentConfigDecorator registerCuiMockComponentWithRenderer() { 188 registerUIComponent(CuiMockComponent.class); 189 registerMockRenderer(CuiMockComponent.FAMILY, CuiMockComponent.RENDERER_TYPE); 190 return this; 191 } 192 193 /** 194 * Registers a {@link CuiMockRenderer} for the given attributes. 195 * 196 * @param family identifying the component-family the renderer is related 197 * to, must not be null 198 * @param rendererType identifying the type of the renderer is related to, must 199 * not be null 200 * @return the {@link ComponentConfigDecorator} itself in order to enable a 201 * fluent-api style usage 202 */ 203 public ComponentConfigDecorator registerMockRenderer(final String family, final String rendererType) { 204 requireNonNull(family, FAMILY_MUST_NOT_BE_NULL); 205 requireNonNull(rendererType, RENDERER_TYPE_MUST_NOT_BE_NULL); 206 facesContext.getRenderKit().addRenderer(family, rendererType, new CuiMockRenderer()); 207 return this; 208 } 209 210 /** 211 * Shorthand for registering a {@link CuiMockRenderer} for 212 * {@link HtmlOutputText}. 213 * 214 * @return the {@link ComponentConfigDecorator} itself in order to enable a 215 * fluent-api style usage 216 */ 217 public ComponentConfigDecorator registerMockRendererForHtmlOutputText() { 218 registerMockRenderer(UIOutput.COMPONENT_FAMILY, TEXT_RENDERER_ID); 219 return this; 220 } 221 222 /** 223 * Shorthand for registering a {@link CuiMockRenderer} for 224 * {@link HtmlSelectBooleanCheckbox}. 225 * 226 * @return the {@link ComponentConfigDecorator} itself in order to enable a 227 * fluent-api style usage 228 */ 229 public ComponentConfigDecorator registerMockRendererForHtmlSelectBooleanCheckbox() { 230 registerMockRenderer(UISelectBoolean.COMPONENT_FAMILY, SELECTBOOLEAN_RENDERER_ID); 231 return this; 232 } 233 234 /** 235 * Shorthand for registering a {@link CuiMockRenderer} for 236 * {@link HtmlSelectOneRadio}. 237 * 238 * @return the {@link ComponentConfigDecorator} itself in order to enable a 239 * fluent-api style usage 240 */ 241 public ComponentConfigDecorator registerMockRendererForHtmlSelectOneRadio() { 242 registerMockRenderer(UISelectOne.COMPONENT_FAMILY, SELECTONE_RENDERER_ID); 243 return this; 244 } 245 246 /** 247 * Shorthand for registering a {@link CuiMockRenderer} for 248 * {@link HtmlInputText}. 249 * 250 * @return the {@link ComponentConfigDecorator} itself in order to enable a 251 * fluent-api style usage 252 */ 253 public ComponentConfigDecorator registerMockRendererForHtmlInputText() { 254 registerMockRenderer(UIInput.COMPONENT_FAMILY, TEXT_RENDERER_ID); 255 return this; 256 } 257 258 /** 259 * Shorthand for registering a {@link CuiMockRenderer} for {@link HtmlForm}. 260 * 261 * @return the {@link ComponentConfigDecorator} itself in order to enable a 262 * fluent-api style usage 263 */ 264 public ComponentConfigDecorator registerMockRendererForHtmlForm() { 265 registerMockRenderer(UIForm.COMPONENT_FAMILY, FORM_RENDERER_ID); 266 return this; 267 } 268 269 /** 270 * Shorthand for registering a {@link CuiMockRenderer} for 271 * {@link HtmlCommandButton}. 272 * 273 * @return the {@link ComponentConfigDecorator} itself in order to enable a 274 * fluent-api style usage 275 */ 276 public ComponentConfigDecorator registerMockRendererForCommandButton() { 277 facesContext.getRenderKit().addRenderer("javax.faces.Command", "javax.faces.Button", new CuiMockRenderer("CommandButton")); 278 return this; 279 } 280 281 /** 282 * Registers a {@link Renderer} 283 * 284 * @param family identifying the component-family the renderer is related 285 * to, must not be null 286 * @param rendererType identifying the type of the renderer is related to, must 287 * not be null 288 * @param renderer the actual renderer to be registered, must not be null 289 * @return the {@link ComponentConfigDecorator} itself in order to enable a 290 * fluent-api style usage 291 */ 292 public ComponentConfigDecorator registerRenderer(final String family, final String rendererType, final Renderer renderer) { 293 requireNonNull(family, FAMILY_MUST_NOT_BE_NULL); 294 requireNonNull(rendererType, RENDERER_TYPE_MUST_NOT_BE_NULL); 295 requireNonNull(renderer, RENDERER_MUST_NOT_BE_NULL); 296 facesContext.getRenderKit().addRenderer(family, rendererType, renderer); 297 return this; 298 } 299 300 /** 301 * Registers a {@link Renderer} The family and rendererType will be derived by 302 * the mandatory {@link FacesRenderer} annotation 303 * 304 * @param renderer the actual renderer to be registered, must not be null, must 305 * provide {@link FacesRenderer} annotation and a no arg public 306 * constructor. 307 * @return the {@link ComponentConfigDecorator} itself in order to enable a 308 * fluent-api style usage 309 */ 310 public ComponentConfigDecorator registerRenderer(final Class<? extends Renderer> renderer) { 311 requireNonNull(renderer, RENDERER_MUST_NOT_BE_NULL); 312 checkArgument(renderer.isAnnotationPresent(FacesRenderer.class), "In order to work this method needs a Renderer annotated with \'javax.faces.render.FacesRenderer\', renderer:" + renderer.getName()); 313 final Renderer instance; 314 instance = new DefaultInstantiator<>(renderer).newInstance(); 315 final var config = renderer.getAnnotation(FacesRenderer.class); 316 return registerRenderer(config.componentFamily(), config.rendererType(), instance); 317 } 318 319 /** 320 * Register a {@link ClientBehavior} for a given behaviorId 321 * 322 * @param behaviorId the id the {@link ClientBehavior} should be registered 323 * with, must not be null 324 * @param behaviorClass the actual type of the {@link ClientBehavior} must not 325 * be null 326 * @return the {@link ComponentConfigDecorator} itself in order to enable a 327 * fluent-api style usage 328 */ 329 public ComponentConfigDecorator registerBehavior(final String behaviorId, final Class<? extends ClientBehavior> behaviorClass) { 330 requireNonNull(behaviorId, BEHAVIOR_ID_MUST_NOT_BE_NULL); 331 requireNonNull(behaviorClass, BEHAVIOR_CLASS_MUST_NOT_BE_NULL); 332 application.addBehavior(behaviorId, behaviorClass.getName()); 333 return this; 334 } 335 336 /** 337 * Register a {@link ClientBehavior}. The behaviorId will be extracted by the 338 * mandatory {@link FacesBehavior} annotation 339 * 340 * @param behaviorClass the actual type of the {@link ClientBehavior} must not 341 * be null and provide the {@link FacesBehavior} annotation 342 * in order to extract the correct behaviorId 343 * @return the {@link ComponentConfigDecorator} itself in order to enable a 344 * fluent-api style usage 345 */ 346 public ComponentConfigDecorator registerBehavior(final Class<? extends ClientBehavior> behaviorClass) { 347 requireNonNull(behaviorClass, BEHAVIOR_CLASS_MUST_NOT_BE_NULL); 348 checkArgument(behaviorClass.isAnnotationPresent(FacesBehavior.class), "In order to work this method needs a ClientBehavior annotated with \'javax.faces.component.behavior.FacesBehavior\', behaviorClass:" + behaviorClass.getName()); 349 return registerBehavior(behaviorClass.getAnnotation(FacesBehavior.class).value(), behaviorClass); 350 } 351 352 /** 353 * Register a composite component to be rendered. 354 * 355 * @param libraryName the library name like 356 * "http://xmlns.jcp.org/jsf/composite/" or " 357 * @param tagName the tag name 358 * @param uiComponent the component that should be returned as mock / 359 * placeholder for the composite component 360 * @return the {@link ComponentConfigDecorator} itself in order to enable a 361 * fluent-api style usage 362 */ 363 public ComponentConfigDecorator registerCompositeComponent(final String libraryName, final String tagName, final UIComponent uiComponent) { 364 if (!(application.getViewHandler() instanceof CuiMockViewHandler)) { 365 application.setViewHandler(new CuiMockViewHandler()); 366 } 367 ((CuiMockViewHandler) application.getViewHandler()).registerCompositeComponent(libraryName, tagName, uiComponent); 368 return this; 369 } 370 371 /** 372 * Add a component to the view root to be found when searching via 373 * {@link UIViewRoot#findComponent(String)}. 374 * 375 * @param expr the expression the component should be found with 376 * @param component the component 377 * @return the {@link ComponentConfigDecorator} itself in order to enable a 378 * fluent-api style usage 379 */ 380 public ComponentConfigDecorator addUiComponent(String expr, UIComponent component) { 381 if (!(facesContext.getViewRoot() instanceof CuiMockUIViewRoot)) { 382 facesContext.setViewRoot(new CuiMockUIViewRoot()); 383 } 384 ((CuiMockUIViewRoot) facesContext.getViewRoot()).addUiComponent(expr, component); 385 return this; 386 } 387 388 @java.lang.SuppressWarnings("all") 389 @lombok.Generated 390 public ComponentConfigDecorator(@NonNull final Application application, @NonNull final FacesContext facesContext) { 391 if (application == null) { 392 throw new java.lang.NullPointerException("application is marked non-null but is null"); 393 } 394 if (facesContext == null) { 395 throw new java.lang.NullPointerException("facesContext is marked non-null but is null"); 396 } 397 this.application = application; 398 this.facesContext = facesContext; 399 } 400}