Conditional.java

  1. /*
  2.  * stringhold
  3.  *
  4.  * Copyright 2022-2024 Christian Kohlschütter
  5.  *
  6.  * Licensed under the Apache License, Version 2.0 (the "License");
  7.  * you may not use this file except in compliance with the License.
  8.  * You may obtain a copy of the License at
  9.  *
  10.  *     http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing, software
  13.  * distributed under the License is distributed on an "AS IS" BASIS,
  14.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15.  * See the License for the specific language governing permissions and
  16.  * limitations under the License.
  17.  */
  18. package com.kohlschutter.stringhold.liqp;

  19. import java.util.Map;

  20. import org.eclipse.jdt.annotation.Nullable;

  21. import liqp.TemplateContext;
  22. import liqp.nodes.LNode;
  23. import liqp.tags.Tag;

  24. /**
  25.  * Sets/gets the conditional state for a certain conditional-key.
  26.  * <p>
  27.  * Examples: <pre><code>
  28.  * {% conditional get: someState %} // false
  29.  * {% conditional set: someState %} // no output
  30.  * {% conditional get: someState %} // true
  31.  * {% conditional clear: someState %} // no output
  32.  * {% conditional get: someState %} // false
  33.  * </code></pre>
  34.  *
  35.  * <b>NOTE:</b> By default, conditionals are {@code true}. The behavior of declaring a conditional
  36.  * tag within a conditionally block is currently undefined.
  37.  *
  38.  * @author Christian Kohlschütter
  39.  * @see Conditionally
  40.  */
  41. public final class Conditional extends Tag {
  42.   static final String ENVMAP_CONDITIONAL_PREFIX = " stringhold.conditional.";
  43.   static final String ENVMAP_SUPPLIED_PREFIX = " stringhold.conditional-supplied.";

  44.   /**
  45.    * Constructs a new "conditional" {@link Tag}.
  46.    */
  47.   public Conditional() {
  48.     super("conditional");
  49.   }

  50.   @SuppressWarnings("PMD.UnnecessaryBoxing")
  51.   @Override
  52.   public Object render(TemplateContext context, LNode... nodes) {
  53.     String args = String.valueOf(nodes[0].render(context));

  54.     String[] parts = args.split(":");
  55.     if (parts.length != 2) {
  56.       throw new IllegalArgumentException(args);
  57.     }

  58.     String command = parts[0];
  59.     String key = parts[1];

  60.     boolean b;
  61.     switch (command) {
  62.       case "set":
  63.         b = true;
  64.         break;
  65.       case "clear":
  66.         b = false;
  67.         break;
  68.       case "get":
  69.         return isConditionalSet(context.getEnvironmentMap(), key);
  70.       default:
  71.         throw new IllegalArgumentException("Illegal conditional command: " + command);
  72.     }

  73.     Map<String, Object> map = context.getEnvironmentMap();
  74.     Boolean supplied = getConditionalSuppliedState(map, key);
  75.     if (supplied != null && supplied.booleanValue() != b) {
  76.       throw new IllegalStateException("Conditional already accessed: " + key);
  77.     }

  78.     setConditional(map, key, b);

  79.     return null;
  80.   }

  81.   /**
  82.    * Checks if the conditional identified by the given key is set.
  83.    *
  84.    * @param envMap The environment map.
  85.    * @param key The key, without its internal prefix.
  86.    * @return {@code true} if set.
  87.    */
  88.   public static boolean isConditionalSet(Map<String, Object> envMap, String key) {
  89.     Object val = envMap.get(Conditional.ENVMAP_CONDITIONAL_PREFIX + key);
  90.     return Boolean.parseBoolean(String.valueOf(val));
  91.   }

  92.   /**
  93.    * Checks if the conditional identified by the given key is set.
  94.    *
  95.    * @param envMap The environment map.
  96.    * @param key The key, without its internal prefix.
  97.    * @param on {@code true} if set, {@code false} if clear.
  98.    */
  99.   public static void setConditional(Map<String, Object> envMap, String key, boolean on) {
  100.     envMap.put(Conditional.ENVMAP_CONDITIONAL_PREFIX + key, on);
  101.   }

  102.   /**
  103.    * Checks the "supplied" state of the conditional identified by the given key.
  104.    *
  105.    * @param envMap The environment map.
  106.    * @param key The key, without its internal prefix.
  107.    * @return {@code false}/{@code true} for the "supply" condition once determined, or {@code null}
  108.    *         if not determined yet.
  109.    */
  110.   public static @Nullable Boolean getConditionalSuppliedState(Map<String, Object> envMap,
  111.       String key) {
  112.     Object val = envMap.get(Conditional.ENVMAP_SUPPLIED_PREFIX + key);
  113.     if (val == null) {
  114.       return null;
  115.     }
  116.     return Boolean.valueOf(String.valueOf(val));
  117.   }
  118. }