Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
fmcarvalho committed Dec 27, 2023
2 parents 4aac871 + f6f206a commit 8853570
Show file tree
Hide file tree
Showing 11 changed files with 567 additions and 269 deletions.
361 changes: 155 additions & 206 deletions Readme.md

Large diffs are not rendered by default.

87 changes: 65 additions & 22 deletions src/main/java/htmlflow/HtmlFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ private HtmlFlow() {
* HtmlContinuation objects containing the static HTML strings and dynamic HTML consumers.
*
* @param template An HtmlTemplate function, which depends on an HtmlView used to create HTMl elements.
* @param isIndented Set indentation on or off.
*/
private static PreprocessingVisitor preprocessing(HtmlTemplate template) {
PreprocessingVisitor pre = new PreprocessingVisitor(true);
HtmlView preView = new HtmlView(() -> pre, false);
private static PreprocessingVisitor preprocessing(HtmlTemplate template, boolean isIndented) {
PreprocessingVisitor pre = new PreprocessingVisitor(isIndented);
HtmlView<?> preView = new HtmlView<>(() -> pre, template, false);
template.resolve(preView);
/**
* NO problem with null model. We are just preprocessing static HTML blocks.
Expand All @@ -57,10 +58,14 @@ private static PreprocessingVisitor preprocessing(HtmlTemplate template) {
preView.getVisitor().resolve(null);
return pre;
}

private static PreprocessingVisitorAsync preprocessingAsync(HtmlTemplate template) {
PreprocessingVisitorAsync pre = new PreprocessingVisitorAsync(true);
HtmlView preView = new HtmlView(() -> pre, false);

/**
* @param template An HtmlTemplate function, which depends on an HtmlView used to create HTMl elements.
* @param isIndented Set indentation on or off.
*/
private static PreprocessingVisitorAsync preprocessingAsync(HtmlTemplate template, boolean isIndented) {
PreprocessingVisitorAsync pre = new PreprocessingVisitorAsync(isIndented);
HtmlView<?> preView = new HtmlView<>(() -> pre, template, false);
template.resolve(preView);
/**
* NO problem with null model. We are just preprocessing static HTML blocks.
Expand All @@ -79,38 +84,76 @@ private static PreprocessingVisitorAsync preprocessingAsync(HtmlTemplate templat
public static HtmlDoc doc(Appendable out){
return new HtmlDoc(new HtmlDocVisitor(out, true));
}

/**
* Creates a HtmlView corresponding to a dynamic HtmlPage with a model and
* default indentation on.
*
* @param out Output PrintStream.
* @param template Function that consumes an HtmlView to produce HTML elements.
* @param <M> Type of the model rendered with this view.
*/
public static <M> HtmlView<M> view(Appendable out, HtmlTemplate template) {
return HtmlFlow.view(out, template, true, false);
}
/**
* Creates a HtmlView corresponding to a dynamic HtmlPage with a model.
*
* @param out Output PrintStream.
* @param template Function that consumes an HtmlView to produce HTML elements.
* @param isIndented Set indentation on or off.
* @param threadSafe Enable the use for thread safety.
* @param <M> Type of the model rendered with this view.
*/
static <M> HtmlView<M> view(Appendable out, HtmlTemplate template, boolean isIndented, boolean threadSafe){
PreprocessingVisitor pre = preprocessing(template, isIndented);
return new HtmlView<>(
(() -> new HtmlViewVisitor(out, isIndented, pre.getFirst())),
template,
threadSafe);
}
/**
* Creates a HtmlView corresponding to a dynamic HtmlPage with a model and
* default indentation on.
*
* @param template Function that consumes an HtmlView to produce HTML elements.
* @param <M> Type of the model rendered with this view.
*/
public static HtmlView view(Appendable out, HtmlTemplate template){
PreprocessingVisitor pre = preprocessing(template);
return new HtmlView(
(() -> new HtmlViewVisitor(out, true, pre.getFirst())),
false); // not thread safe by default
public static <M> HtmlView<M> view(HtmlTemplate template) {
return HtmlFlow.view(template, true, false);
}
/**
* Creates a HtmlView corresponding to a dynamic HtmlPage with a model.
*
* @param template Function that consumes an HtmlView to produce HTML elements.
* @param isIndented Set indentation on or off.
* @param threadSafe Enable the use for thread safety.
* @param <M> Type of the model rendered with this view.
*/
public static HtmlView view(HtmlTemplate template){
PreprocessingVisitor pre = preprocessing(template);
return new HtmlView(
(() -> new HtmlViewVisitor(new StringBuilder(), true, pre.getFirst())),
false); // not thread safe by default
static <M> HtmlView<M> view(HtmlTemplate template, boolean isIndented, boolean threadSafe){
PreprocessingVisitor pre = preprocessing(template, isIndented);
return new HtmlView<>(
(() -> new HtmlViewVisitor(new StringBuilder(), isIndented, pre.getFirst())),
template,
threadSafe);
}
/**
* Creates a HtmlViewAsync corresponding to a dynamic HtmlPage with an asynchronous model and
* default indentation on.
* @param template Function that consumes an HtmlView to produce HTML elements.
* @param <M> Type of the model rendered with this view.
*/
public static <M> HtmlViewAsync<M> viewAsync(HtmlTemplate template) {
return HtmlFlow.viewAsync(template, true, false);
}

/**
* Creates a HtmlViewAsync corresponding to a dynamic HtmlPage with an asynchronous model.
*
* @param template Function that consumes an HtmlView to produce HTML elements.
* @param isIndented Set indentation on or off.
* @param <M> Type of the model rendered with this view.
*/
public static HtmlViewAsync viewAsync(HtmlTemplate template){
PreprocessingVisitorAsync pre = preprocessingAsync(template);
return new HtmlViewAsync(new HtmlViewVisitorAsync(true, pre.getFirst()));
static <M> HtmlViewAsync<M> viewAsync(HtmlTemplate template, boolean isIndented, boolean threadSafe){
PreprocessingVisitorAsync pre = preprocessingAsync(template, isIndented);
return new HtmlViewAsync<>(new HtmlViewVisitorAsync(isIndented, pre.getFirst()), template, threadSafe);
}
}
26 changes: 17 additions & 9 deletions src/main/java/htmlflow/HtmlView.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,15 @@
/**
* Dynamic views can be bound to a Model object.
*
* @param <M> Type of the model rendered with this view.
*
* @author Miguel Gamboa, Luís Duare
*/
public class HtmlView extends HtmlPage {
public class HtmlView<M> extends HtmlPage {
/**
* Function that consumes an HtmlView to produce HTML elements.
*/
private final HtmlTemplate template;
/**
* This field is like a union with the threadLocalVisitor, being used alternatively.
* For non thread safe scenarios Visitors maybe shared concurrently by multiple threads.
Expand All @@ -57,9 +63,11 @@ public class HtmlView extends HtmlPage {
*/
HtmlView(
Supplier<HtmlVisitor> visitorSupplier,
HtmlTemplate template,
boolean threadSafe)
{
this.visitorSupplier = visitorSupplier;
this.template = template;
this.threadSafe = threadSafe;
if(threadSafe) {
this.visitor = null;
Expand All @@ -75,7 +83,7 @@ public final Html<HtmlPage> html() {
return new Html<>(this);
}

public HtmlView threadSafe(){
public HtmlView<M> threadSafe(){
return clone(visitorSupplier, true);
}

Expand All @@ -86,7 +94,7 @@ public HtmlVisitor getVisitor() {
: visitor;
}

public HtmlView setOut(Appendable out) {
public HtmlView<M> setOut(Appendable out) {
getVisitor().setAppendable(out);
return this;
}
Expand All @@ -100,14 +108,14 @@ public String render() {
return render(null);
}

public String render(Object model) {
public String render(M model) {
StringBuilder str = ((StringBuilder) getVisitor().out());
str.setLength(0);
getVisitor().resolve(model);
return str.toString();
}

public void write(Object model) {
public void write(M model) {
getVisitor().resolve(model);
}

Expand All @@ -122,19 +130,19 @@ public void write() {
* @param visitorSupplier
* @param threadSafe
*/
protected final HtmlView clone(
protected final HtmlView<M> clone(
Supplier<HtmlVisitor> visitorSupplier,
boolean threadSafe)
{
return new HtmlView(visitorSupplier, threadSafe);
return new HtmlView<>(visitorSupplier, template, threadSafe);
}

/**
* Returns a new instance of HtmlFlow with the same properties of this object
* but with indented set to the value of isIndented parameter.
*/
@Override
public final HtmlView setIndented(boolean isIndented) {
return clone(() -> getVisitor().clone(isIndented), false);
public final HtmlView<M> setIndented(boolean isIndented) {
return HtmlFlow.view(getVisitor().out(), template, isIndented, threadSafe);
}
}
39 changes: 25 additions & 14 deletions src/main/java/htmlflow/HtmlViewAsync.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,30 @@
import java.util.concurrent.CompletableFuture;

/**
* Dynamic views can be bound to a domain object within an asynchronous context with the usage of {@link org.reactivestreams.Publisher}.
* Asynchronous views can be bound to a domain object with an asynchronous API.
*
* @param <M> Type of the model rendered with this view.
*
* @author Pedro Fialho
*/
public class HtmlViewAsync extends HtmlPage {
public class HtmlViewAsync<M> extends HtmlPage {
/**
* Function that consumes an HtmlView to produce HTML elements.
*/
private final HtmlTemplate template;

private final HtmlViewVisitorAsync visitor;

private final boolean threadSafe;

HtmlViewAsync(HtmlViewVisitorAsync visitor) {
this(visitor, true);
HtmlViewAsync(HtmlViewVisitorAsync visitor, HtmlTemplate template) {
this(visitor, template, true);
}

public HtmlViewAsync(HtmlViewVisitorAsync visitor, boolean safe) {
public HtmlViewAsync(HtmlViewVisitorAsync visitor, HtmlTemplate template, boolean safe) {
this.visitor = visitor;
threadSafe = safe;
this.template = template;
this.threadSafe = safe;
}

@Override
Expand All @@ -56,8 +63,8 @@ public final Html<HtmlPage> html() {
}

@Override
public HtmlPage setIndented(boolean isIndented) {
return new HtmlViewAsync(visitor.clone(isIndented));
public HtmlViewAsync<M> setIndented(boolean isIndented) {
return HtmlFlow.viewAsync(template, isIndented, threadSafe);
}

@Override
Expand All @@ -71,24 +78,28 @@ public String getName() {
}

@Override
public HtmlViewAsync threadSafe(){
return new HtmlViewAsync(visitor);
public HtmlViewAsync<M> threadSafe(){
return new HtmlViewAsync<>(visitor, template);
}

public HtmlViewAsync threadUnsafe(){
return new HtmlViewAsync(visitor, false);
public HtmlViewAsync<M> threadUnsafe(){
return new HtmlViewAsync<>(visitor, template, false);
}


public final CompletableFuture<Void> writeAsync(Appendable out, Object model) {
public final CompletableFuture<Void> writeAsync(Appendable out, M model) {
if (threadSafe) {
return visitor.clone(out).finishedAsync(model);
}
visitor.setAppendable(out);
return visitor.finishedAsync(model);
}

public final CompletableFuture<String> renderAsync(Object model) {
public final CompletableFuture<String> renderAsync() {
return renderAsync(null);
}

public final CompletableFuture<String> renderAsync(M model) {
StringBuilder str = new StringBuilder();
return writeAsync(str, model).thenApply( nothing -> str.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@

import htmlflow.visitor.HtmlVisitor;

import static java.lang.System.lineSeparator;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.joining;

/**
* HtmlContinuation for a static HTML block.
*/
Expand All @@ -52,11 +48,8 @@ protected final void emitHtml(Object model) {

@Override
public HtmlContinuation copy(HtmlVisitor v) {
String block = v.isIndented
? staticHtmlBlock
: stream(staticHtmlBlock.split(lineSeparator())).map(String::trim).collect(joining());
return new HtmlContinuationSyncStatic(
block,
staticHtmlBlock,
v,
next != null ? next.copy(v) : null); // call copy recursively
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/htmlflow/visitor/HtmlVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ protected final void visitParentOnVoidElements(){

@Override
public final void visitAttribute(String attributeName, String attributeValue) {
if(isClosed)
throw new IllegalStateException("Cannot add attributes after!!!");
addAttribute(out, attributeName, attributeValue);
}

Expand Down
6 changes: 2 additions & 4 deletions src/test/java/htmlflow/test/TestAsyncViewInConcurrency.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ void check_asyncview_processing_in_sequential_tasks_and_unsafe_view() {
/**
* Arrange View
*/
final HtmlViewAsync view = HtmlFlow.viewAsync(TestAsyncView::testAsyncModel).threadUnsafe();
final HtmlViewAsync<AsyncModel> view = HtmlFlow.<AsyncModel>viewAsync(TestAsyncView::testAsyncModel).threadUnsafe();
/**
* Act and Assert
* Since Stream is Lazy then there is a vertical processing and a sequential execution between tasks.
Expand All @@ -48,7 +48,7 @@ void check_asyncview_processing_in_concurrent_tasks_and_parallel_threads() {
/**
* Arrange View
*/
final HtmlViewAsync view = HtmlFlow.viewAsync(TestAsyncView::testAsyncModel).threadSafe();
final HtmlViewAsync<AsyncModel> view = HtmlFlow.<AsyncModel>viewAsync(TestAsyncView::testAsyncModel).threadSafe();
/**
* Act and Assert
* Collects to dispatch resolution through writeAsync() concurrently.
Expand All @@ -61,7 +61,6 @@ void check_asyncview_processing_in_concurrent_tasks_and_parallel_threads() {
.forEach(cf ->assertHtml(cf.join()));
}


private static void assertHtml(String html) {
Iterator<String> actual = Utils
.NEWLINE
Expand All @@ -75,6 +74,5 @@ private static void assertHtml(String html) {
assertEquals(expected, next);
});
assertFalse(actual.hasNext());

}
}
Loading

0 comments on commit 8853570

Please sign in to comment.