001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.console.freemarker;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.*;
022import java.util.stream.*;
023
024import org.apache.juneau.commons.http.*;
025import org.apache.juneau.marshall.collections.*;
026
027/**
028 * The built-in {@code markers} decorator: vertical mark lines at x values from a list, a same-origin {@code src}, a
029 * {@code data:} topic, or the current time.  The lines ride a carrier series {@code juneau-markers-<n>}.
030 *
031 * <h5 class='section'>Example:</h5>
032 * <p class='bjava'>
033 *    Markers.<jsm>from</jsm>(<js>"/rest/releases"</js>).at(<js>"date"</js>).label(<js>"name"</js>).onClick(Topics.<jsm>custom</jsm>(<js>"app"</js>, <js>"release"</js>));
034 *    Markers.<jsm>now</jsm>(<js>"Today"</js>);
035 * </p>
036 *
037 * @since 10.0.0
038 */
039public final class Markers implements Decorator {
040
041   private static final Set<String> STYLES = Set.of("solid", "dashed", "dotted");
042
043   private final JsonMap m = JsonMap.of("use", "markers");
044
045   private Markers() {}
046
047   /** @param path A same-origin path returning rows. @return A new decorator. */
048   public static Markers from(String path) {
049      if (! SafePathTemplate.isSameOriginPath(path))
050         throw iaex("Markers src must be a same-origin path; got '%s'.", path);
051      var x = new Markers();
052      x.m.put("src", path);
053      return x;
054   }
055
056   /** @param rows Inline rows {@code {at, label?, meta?}}. @return A new decorator. */
057   public static Markers values(List<? extends Map<String,?>> rows) {
058      var x = new Markers();
059      x.m.put("values", copyRows(rows));
060      return x;
061   }
062
063   /** @param dataTopic A {@code data:} topic ({@code Topics.data(...)}). @return A new decorator. */
064   public static Markers source(String dataTopic) {
065      var x = new Markers();
066      x.m.put("source", dataTopicOf("Markers", dataTopic));
067      return x;
068   }
069
070   /** @param label The label of a mark line at the current time. @return A new decorator. */
071   public static Markers now(String label) {
072      var x = new Markers();
073      x.m.put("now", JsonMap.of("label", label));
074      return x;
075   }
076
077   /** @param field The row field holding the x value. @return This object. */
078   public Markers at(String field) { m.put("at", field); return this; }
079
080   /** @param field The row field holding the line label. @return This object. */
081   public Markers label(String field) { m.put("label", field); return this; }
082
083   /** @param v {@code solid}, {@code dashed} or {@code dotted}. @return This object. */
084   public Markers style(String v) {
085      if (! STYLES.contains(v))
086         throw iaex("Markers style '%s' must be 'solid', 'dashed' or 'dotted'.", v);
087      m.put("style", v);
088      return this;
089   }
090
091   /** @param topic A custom topic the card publishes a click on a line to. @return This object. */
092   public Markers onClick(String topic) { m.put("topic", topic); return this; }
093
094   @Override
095   public JsonMap toJson() { return new JsonMap(m); }
096
097   static List<Map<String,Object>> copyRows(List<? extends Map<String,?>> rows) {
098      if (rows == null)
099         throw iaex("Decorator values must not be null.");
100      return rows.stream().<Map<String,Object>>map(LinkedHashMap::new).collect(Collectors.toCollection(ArrayList::new));
101   }
102
103   static String dataTopicOf(String who, String topic) {
104      if (topic == null || ! topic.startsWith("data:") || topic.length() == 5)
105         throw iaex("%s source must be a data: topic; got '%s'.", who, topic);
106      return topic;
107   }
108}