All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.foreach.across.modules.web.ui.ViewElementBuilder Maven / Gradle / Ivy

/*
 * Copyright 2019 the original author or authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.foreach.across.modules.web.ui;

import lombok.NonNull;

import java.util.Collection;
import java.util.Collections;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Base interface to create a single {@link ViewElement} instance.
 * Usually used to build an entire hierarchy of elements by calling {@link #build(ViewElementBuilderContext)} on the
 * top-most element. Building a {@link ViewElement} requires a {@link ViewElementBuilderContext} and often a
 * single builder context is used to build many elements.
 * 

* If you call {@link #build()} without manually specifying a builder context, a global context will be retrieved * using {@link ViewElementBuilderContext#retrieveGlobalBuilderContext()} and if none is available, a new * {@link DefaultViewElementBuilderContext} will be used instead. *

* For performance it is often best to manage the lifecycle of a {@link ViewElementBuilderContext} yourself, * so you don't have unnecessary creation and can optimize contextual data sharing. * * @author Arne Vandamme */ @FunctionalInterface public interface ViewElementBuilder { /** * Build the {@link ViewElement} using the globally available {@link ViewElementBuilderContext}, * this will use {@link ViewElementBuilderContext#retrieveGlobalBuilderContext()} to get the global context. *

* If none is returned, a new {@link DefaultViewElementBuilderContext} will be used instead. *

* Use this method sparingly, usually only for the single top-level build of a {@link ViewElement}. * The creation of a {@link ViewElementBuilderContext} can be relatively costly, performance-wise it is usually * better if you call {@link #build(ViewElementBuilderContext)} with a predefined or {@link ViewElementBuilderContext}, * or at least ensure you have a global context available. * * @return view element * @see ViewElementBuilderContext#retrieveGlobalBuilderContext() */ default T build() { ViewElementBuilderContext globalBuilderContext = ViewElementBuilderContext .retrieveGlobalBuilderContext() .orElseGet( DefaultViewElementBuilderContext::new ); return build( globalBuilderContext ); } /** * Builds the actual element. * * @param builderContext provides the context for this build event * @return instance to render the element. */ T build( ViewElementBuilderContext builderContext ); /** * Chain a {@link ViewElementPostProcessor} to the result of this builder. * This will return a new builder instance that applies the post processor to the element generated. *

* Explicitly supports {@code null} argument values, which mean do nothing and will actually * return the same builder. * * @param postProcessor to apply on the builder result * @return new builder with the post processor chained to it */ default ViewElementBuilder andThen( ViewElementPostProcessor postProcessor ) { if ( postProcessor != null ) { ViewElementBuilder self = this; return builderContext -> { T element = self.build( builderContext ); postProcessor.postProcess( builderContext, element ); return element; }; } return this; } /** * Map the {@link ViewElement} that this builder returns to another type. * Creates a new builder returning the resulting element. *

* If you need access to the {@link ViewElementBuilderContext} use {@link #map(BiFunction)} instead. * * @param mappingFunction to apply to the generated element * @param type of the new element returned * @return new builder instance * @see #map(BiFunction) */ default ViewElementBuilder map( @NonNull Function mappingFunction ) { return builderContext -> { T element = this.build( builderContext ); return mappingFunction.apply( element ); }; } /** * Map the {@link ViewElement} that this builder returns to another type. * Creates a new builder returning the resulting element. * * @param mappingFunction to apply to the generated element * @param type of the new element returned * @return new builder instance * @see #map(Function) */ default ViewElementBuilder map( @NonNull BiFunction mappingFunction ) { return builderContext -> { T element = this.build( builderContext ); return mappingFunction.apply( builderContext, element ); }; } // don't use yet - work in progress @Deprecated default ViewElementBuilder doWith( Wither... operations ) { return null; } default ViewElementBuilder postProcess( ViewElementPostProcessor postProcessors ) { return postProcess( Collections.singleton( postProcessors ) ); } default ViewElementBuilder postProcess( Collection> postProcessors ) { return builderContext -> { T element = build( builderContext ); postProcessors.forEach( processor -> processor.postProcess( builderContext, element ) ); return element; }; } static ViewElementBuilder of( Supplier supplier ) { return ( builderContext ) -> supplier.get(); } static ViewElementBuilder of( Function supplier ) { return supplier::apply; } @FunctionalInterface interface Wither { void applyTo( T builder ); // div( children(), postProcess() ) // ViewElementBuilder.of( () -> new NodeViewElement("div" ).with( text( "hello" ), attribute( "hm" ) ) } }