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

com.github.jknack.handlebars.helper.ConditionalHelpers Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/**
 * Copyright (c) 2012-2015 Edgar Espina
 *
 * This file is part of Handlebars.java.
 *
 * 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.github.jknack.handlebars.helper;

import static org.apache.commons.lang3.Validate.isTrue;

import java.io.IOException;
import java.util.Objects;

import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Options;
import com.github.jknack.handlebars.TagType;

/**
 * Implementation of equals, greater, lessThan, and, or, etc.. operators.
 *
 * @author edgar
 * @since 4.0.7
 */
public enum ConditionalHelpers implements Helper {

  /**
   * Test if two elements are equals. Usage:
   *
   * Render 'yes' or 'no':
   * 
   *   {{#eq a b}}
   *     yes
   *   {{else}}
   *     no
   *   {{/eq}}
   * 
* * Render 'true' or 'false': *
   *   {{eq a b}}
   * 
* * Render 'y' or 'n': *
   *   {{eq a b yes='y' no='n'}}
   * 
*/ eq { @Override public Object apply(final Object a, final Options options) throws IOException { Object b = options.param(0, null); boolean result = eq(a, b); if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }, /** * Test if two elements are NOT equals. Usage: * * Render 'yes' or 'no': *
   *   {{#neq a b}}
   *     yes
   *   {{else}}
   *     no
   *   {{/neq}}
   * 
* * Render 'true' or 'false': *
   *   {{neq a b}}
   * 
* * Render 'y' or 'n': *
   *   {{neq a b yes='y' no='n'}}
   * 
*/ neq { @Override public Object apply(final Object a, final Options options) throws IOException { Object b = options.param(0, null); boolean result = !eq(a, b); if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }, /** * Greater operator (arguments must be {@link Comparable} elements. Usage: * * Render 'yes' or 'no': *
   *   {{#gt a b}}
   *     yes
   *   {{else}}
   *     no
   *   {{/gt}}
   * 
* * Render 'true' or 'false': *
   *   {{gt a b}}
   * 
* * Render 'y' or 'n': *
   *   {{neq a b yes='y' no='n'}}
   * 
*/ gt { @Override public Object apply(final Object a, final Options options) throws IOException { boolean result = cmp(a, options.param(0, null)) > 0; if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }, /** * Greater or equal operator (arguments must be {@link Comparable} elements. Usage: * * Render 'yes' or 'no': *
   *   {{#gte a b}}
   *     yes
   *   {{else}}
   *     no
   *   {{/gte}}
   * 
* * Render 'true' or 'false': *
   *   {{gte a b}}
   * 
* * Render 'y' or 'n': *
   *   {{gte a b yes='y' no='n'}}
   * 
*/ gte { @Override public Object apply(final Object a, final Options options) throws IOException { boolean result = cmp(a, options.param(0, null)) >= 0; if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }, /** * Less than operator (arguments must be {@link Comparable} elements. Usage: * * Render 'yes' or 'no': *
   *   {{#lt a b}}
   *     yes
   *   {{else}}
   *     no
   *   {{/lt}}
   * 
* * Render 'true' or 'false': *
   *   {{lt a b}}
   * 
* * Render 'y' or 'n': *
   *   {{lt a b yes='y' no='n'}}
   * 
*/ lt { @Override public Object apply(final Object a, final Options options) throws IOException { boolean result = cmp(a, options.param(0, null)) < 0; if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }, /** * Less or equal operator (arguments must be {@link Comparable} elements. Usage: * * Render 'yes' or 'no': *
   *   {{#lte a b}}
   *     yes
   *   {{else}}
   *     no
   *   {{/lte}}
   * 
* * Render 'true' or 'false': *
   *   {{lte a b}}
   * 
* * Render 'y' or 'n': *
   *   {{lte a b yes='y' no='n'}}
   * 
*/ lte { @Override public Object apply(final Object a, final Options options) throws IOException { boolean result = cmp(a, options.param(0, null)) <= 0; if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }, /** * And operator. * * Truthiness of arguments is determined by {@link Handlebars.Utils#isEmpty(Object)}, so this * helper can be used with non-boolean values. * * Usage: * * Render 'yes' or 'no': *
   *   {{#and a b}}
   *     yes
   *   {{else}}
   *     no
   *   {{/and}}
   * 
* * Multiple arguments are supported too: *
   *   {{#and a b c d}}
   *     yes
   *   {{else}}
   *     no
   *   {{/and}}
   * 
* * Render 'true' or 'false': *
   *   {{and a b}}
   * 
* * Render 'y' or 'n': *
   *   {{and a b yes='y' no='n'}}
   * 
*/ and { @Override public Object apply(final Object context, final Options options) throws IOException { boolean result = !Handlebars.Utils.isEmpty(context); if (result) { for (int i = 0; i < options.params.length && result; i++) { result = !Handlebars.Utils.isEmpty(options.params[i]); } } if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }, /** * Or operator. * * Truthiness of arguments is determined by {@link Handlebars.Utils#isEmpty(Object)}, so this * helper can be used with non-boolean values. * * Usage: * * Render 'yes' or 'no': *
   *   {{#or a b}}
   *     yes
   *   {{else}}
   *     no
   *   {{/or}}
   * 
* * Multiple arguments are supported too: *
   *   {{#or a b c d}}
   *     yes
   *   {{else}}
   *     no
   *   {{/or}}
   * 
* * Render 'true' or 'false': *
   *   {{or a b}}
   * 
* * Render 'y' or 'n': *
   *   {{or a b yes='y' no='n'}}
   * 
*/ or { @Override public Object apply(final Object context, final Options options) throws IOException { boolean result = !Handlebars.Utils.isEmpty(context); if (!result) { int i = 0; while (!result && i < options.params.length) { result = !Handlebars.Utils.isEmpty(options.params[i++]); } } if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }, /** * Not operator. * * Truthiness of arguments is determined by {@link Handlebars.Utils#isEmpty(Object)}, so this * helper can be used with non-boolean values. * * Usage: * * Render 'yes' or 'no': *
   *   {{#not a}}
   *     yes
   *   {{else}}
   *     no
   *   {{/not}}
   * 
* * Render 'true' or 'false': *
   *   {{not a}}
   * 
* * Render 'y' or 'n': *
   *   {{not a yes='y' no='n'}}
   * 
*/ not { @Override public Object apply(final Object context, final Options options) throws IOException { boolean result = Handlebars.Utils.isEmpty(context); if (options.tagType == TagType.SECTION) { return result ? options.fn() : options.inverse(); } return result ? options.hash("yes", true) : options.hash("no", false); } }; /** * Compare to object as comparables. * * @param a A comparable object. * @param b Another comparable object. * @return Int. */ protected int cmp(final Object a, final Object b) { try { isTrue(a instanceof Comparable, "Not a comparable: " + a); isTrue(b instanceof Comparable, "Not a comparable: " + b); return ((Comparable) a).compareTo(b); } catch (ClassCastException x) { return Double.compare(toDoubleOrError(a, x), toDoubleOrError(b, x)); } } /** * Compare two values. Number equality are treated as equals on they are like: 2 vs 2.0 * * @param a First value. * @param b Second value. * @return True when equals. */ protected boolean eq(final Object a, final Object b) { boolean value = Objects.equals(a, b); if (!value) { if (a instanceof Number && b instanceof Number) { // int vs double: 2 vs 2.0 return ((Number) a).doubleValue() == ((Number) b).doubleValue(); } } return value; } /** * Generate double from value or throw existing exception. * @param value Value to cast. * @param x Exception. * @return Double. */ private double toDoubleOrError(final Object value, final RuntimeException x) { if (value instanceof Double) { return (Double) value; } if (value instanceof Number) { return ((Number) value).doubleValue(); } throw x; } }