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 java.io.*;
020import java.util.*;
021
022import org.apache.juneau.marshall.collections.*;
023import org.apache.juneau.marshall.json5.*;
024
025import freemarker.core.*;
026import freemarker.template.*;
027
028/**
029 * The {@code <@facts>} FreeMarker twin of the {@code <@console facts=>} attribute: a JSON5 object body, merged
030 * last-wins with any other chrome-side facts.  Repeatable; the one strict check against Java-seeded facts happens
031 * once, at {@code </@console>}.
032 *
033 * <h5 class='section'>Example:</h5>
034 * <p class='bcode'>
035 *    &lt;@facts&gt;{"buildVersion": "1.0"}&lt;/@facts&gt;
036 * </p>
037 *
038 * @since 10.0.0
039 */
040public final class FactsDirectiveModel implements TemplateDirectiveModel {
041
042   /** The shared-variable name this directive registers under. */
043   public static final String NAME = "facts";
044
045   FactsDirectiveModel() {}
046
047   @Override
048   @SuppressWarnings({
049      "unchecked" // FreeMarker's raw params Map is String-keyed by contract.
050   })
051   public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
052         TemplateDirectiveBody body) throws TemplateException, IOException {
053      Map<String, TemplateModel> p = params;
054      if (p.containsKey("format"))
055         throw FtlAttrLists.reject("<@facts> has no format= attribute.");
056      FtlAttrLists.rejectUnknown(p, NAME, Set.of());
057
058      var cap = PageCapture.get(env);
059      if (cap == null || ! cap.consoleOpen)
060         throw FtlAttrLists.reject("<@facts> must be nested inside <@console>.");
061
062      var markup = PageCapture.render(body);
063      if (markup.isBlank())
064         return;
065      JsonMap parsed;
066      try {
067         parsed = Json5Parser.DEFAULT.read(markup.trim(), JsonMap.class);
068      } catch (org.apache.juneau.marshall.parser.ParseException e) {
069         throw FtlAttrLists.reject("<@facts> body is not valid JSON5: " + e.getMessage());
070      }
071      cap.mergeChromeFacts(parsed);
072   }
073}