use of org.thymeleaf.model.IText in project thymeleaf by thymeleaf.
the class IteratedGatheringModelProcessable method computeIterationModels.
/*
* Internal-only method, meant to reshape the gathered model so that white space is adequately handled
* during iteration. As a result, this method will produce different Model object for the first, the middle
* and the last iterations.
*/
private IterationModels computeIterationModels(final IterationWhiteSpaceHandling iterationWhiteSpaceHandling) {
/*
* Nothing to iterate
*/
if (iterationWhiteSpaceHandling == IterationWhiteSpaceHandling.ZERO_ITER) {
return IterationModels.EMPTY;
}
/*
* Get the originally gathered model. This will serve as a base for any needed modifications
*/
final Model innerModel = getInnerModel();
final int gatheredModelSize = innerModel.size();
/*
* If there is only one iteration, we need to perform no modifications at all, whichever the template mode
*/
if (iterationWhiteSpaceHandling == IterationWhiteSpaceHandling.SINGLE_ITER) {
return new IterationModels(innerModel, innerModel, innerModel);
}
/*
* If template mode is a markup one, we will only need to take care of the existence of a preceding white space
*/
if (!this.templateMode.isText()) {
if (this.precedingWhitespace != null) {
final Model modelWithWhiteSpace = new Model(innerModel);
modelWithWhiteSpace.insert(0, this.precedingWhitespace);
return new IterationModels(innerModel, modelWithWhiteSpace, modelWithWhiteSpace);
}
return new IterationModels(innerModel, innerModel, innerModel);
}
if (innerModel.size() <= 2) {
// This does only contain the template open + close events -- nothing to be done
return new IterationModels(innerModel, innerModel, innerModel);
}
int firstBodyEventCutPoint = -1;
int lastBodyEventCutPoint = -1;
// we know there is at least one body event
final ITemplateEvent firstBodyEvent = innerModel.get(1);
Text firstTextBodyEvent = null;
if (innerModel.get(0) instanceof OpenElementTag && firstBodyEvent instanceof IText) {
firstTextBodyEvent = Text.asEngineText((IText) firstBodyEvent);
final int firstTextEventLen = firstTextBodyEvent.length();
int i = 0;
char c;
while (i < firstTextEventLen && firstBodyEventCutPoint < 0) {
c = firstTextBodyEvent.charAt(i);
if (c == '\n') {
firstBodyEventCutPoint = i + 1;
// we've already assigned the value we were looking for
break;
} else if (Character.isWhitespace(c)) {
i++;
continue;
} else {
// We will not be able to perform any whitespace reduction here
break;
}
}
}
final ITemplateEvent lastBodyEvent = innerModel.get(gatheredModelSize - 2);
Text lastTextBodyEvent = null;
if (firstBodyEventCutPoint >= 0 && innerModel.get(gatheredModelSize - 1) instanceof CloseElementTag && lastBodyEvent instanceof IText) {
lastTextBodyEvent = Text.asEngineText((IText) lastBodyEvent);
final int lastTextEventLen = lastTextBodyEvent.length();
int i = lastTextEventLen - 1;
char c;
while (i >= 0 && lastBodyEventCutPoint < 0) {
c = lastTextBodyEvent.charAt(i);
if (c == '\n') {
lastBodyEventCutPoint = i + 1;
// we've already assigned the value we were looking for
break;
} else if (Character.isWhitespace(c)) {
i--;
continue;
} else {
// We will not be able to perform any whitespace reduction here
break;
}
}
}
/*
* If there is no reason to perform any modifications, just use the gathered model
*/
if (firstBodyEventCutPoint < 0 || lastBodyEventCutPoint < 0) {
// We don't have the scenario required for performing the needed whitespace collapsing operation
return new IterationModels(innerModel, innerModel, innerModel);
}
if (firstBodyEvent == lastBodyEvent) {
// If the first and the last event are actually the same, we need to take better care of how we manage whitespace
final Text textForFirst = new Text(firstTextBodyEvent.subSequence(0, lastBodyEventCutPoint));
final Text textForMiddle = new Text(firstTextBodyEvent.subSequence(firstBodyEventCutPoint, lastBodyEventCutPoint));
final Text textForLast = new Text(firstTextBodyEvent.subSequence(firstBodyEventCutPoint, firstTextBodyEvent.length()));
final Model modelFirst = new Model(innerModel);
modelFirst.replace(1, textForFirst);
final Model modelMiddle = new Model(innerModel);
modelMiddle.replace(1, textForMiddle);
final Model modelLast = new Model(innerModel);
modelLast.replace(1, textForLast);
return new IterationModels(modelFirst, modelMiddle, modelLast);
}
// At this point, we know the first and last body events are different objects
final Model modelFirst = new Model(innerModel);
final Model modelMiddle = new Model(innerModel);
final Model modelLast = new Model(innerModel);
if (firstBodyEventCutPoint > 0) {
final Text headTextForMiddleAndMax = new Text(firstTextBodyEvent.subSequence(firstBodyEventCutPoint, firstTextBodyEvent.length()));
modelMiddle.replace(1, headTextForMiddleAndMax);
modelLast.replace(1, headTextForMiddleAndMax);
}
if (lastBodyEventCutPoint < lastTextBodyEvent.length()) {
final Text tailTextForFirstAndMiddle = new Text(lastTextBodyEvent.subSequence(0, lastBodyEventCutPoint));
modelFirst.replace(gatheredModelSize - 2, tailTextForFirstAndMiddle);
modelMiddle.replace(gatheredModelSize - 2, tailTextForFirstAndMiddle);
}
return new IterationModels(modelFirst, modelMiddle, modelLast);
}
use of org.thymeleaf.model.IText in project thymeleaf by thymeleaf.
the class ProcessorTemplateHandler method handleText.
@Override
public void handleText(final IText itext) {
/*
* If processing is stopped, we should queue this for later handling
* In theory, given the origin of events (parser or cache) should get stopped immediately, this should
* only happen if a pre-processor is producing additional events.
*/
if (this.throttleEngine && this.flowController.stopProcessing) {
queueEvent(itext);
return;
}
/*
* CHECK WHETHER WE ARE GATHERING AN ELEMENT's MODEL
*/
if (!this.modelController.shouldProcessText(itext)) {
return;
}
/*
* FAIL FAST in case this structure has no associated processors.
*/
if (this.textProcessors.length == 0) {
this.next.handleText(itext);
return;
}
/*
* CAST EVENT TO ENGINE-SPECIFIC IMPLEMENTATION
*/
Text text = Text.asEngineText(itext);
/*
* DECLARE VARIABLES THAT MIGHT BE NEEDED FOR TAKING ACTIONS INSTRUCTED BY THE PROCESSORS
*/
boolean discardEvent = false;
Model model = null;
ITemplateHandler modelHandler = this;
final TextStructureHandler structureHandler = this.textStructureHandler;
/*
* EXECUTE PROCESSORS
*/
for (int i = 0; !discardEvent && i < this.textProcessors.length; i++) {
structureHandler.reset();
this.textProcessors[i].process(this.context, text, structureHandler);
if (structureHandler.setText) {
text = new Text(structureHandler.setTextValue);
} else if (structureHandler.replaceWithModel) {
model = resetModel(model, true);
model.addModel(structureHandler.replaceWithModelValue);
modelHandler = structureHandler.replaceWithModelProcessable ? this : this.next;
discardEvent = true;
} else if (structureHandler.removeText) {
model = null;
discardEvent = true;
}
}
/*
* PROCESS THE REST OF THE HANDLER CHAIN
*/
if (!discardEvent) {
this.next.handleText(text);
}
/*
* PROCESS THE QUEUED MODEL IF NEEDED (or handle it as pending if we are throttling the engine)
*/
if (model == null || model.size() == 0) {
return;
}
if (!this.throttleEngine) {
model.process(modelHandler);
} else {
queueProcessable(new SimpleModelProcessable(model, modelHandler, this.flowController));
}
}
Aggregations