1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.pattern.parser;
15  
16  import java.util.Map;
17  import java.util.function.Supplier;
18  
19  import ch.qos.logback.core.pattern.CompositeConverter;
20  import ch.qos.logback.core.pattern.Converter;
21  import ch.qos.logback.core.pattern.DynamicConverter;
22  import ch.qos.logback.core.pattern.LiteralConverter;
23  import ch.qos.logback.core.spi.ContextAwareBase;
24  import ch.qos.logback.core.status.ErrorStatus;
25  import ch.qos.logback.core.util.OptionHelper;
26  
27  class Compiler<E> extends ContextAwareBase {
28  
29      Converter<E> head;
30      Converter<E> tail;
31      final Node top;
32      final Map<String, Supplier<DynamicConverter>> converterMap;
33  
34      Compiler(final Node top, final Map<String, Supplier<DynamicConverter>> converterMap) {
35          this.top = top;
36          this.converterMap = converterMap;
37      }
38  
39      Converter<E> compile() {
40          head = tail = null;
41          for (Node n = top; n != null; n = n.next) {
42              switch (n.type) {
43              case Node.LITERAL:
44                  addToList(new LiteralConverter<E>((String) n.getValue()));
45                  break;
46              case Node.COMPOSITE_KEYWORD:
47                  CompositeNode cn = (CompositeNode) n;
48                  CompositeConverter<E> compositeConverter = createCompositeConverter(cn);
49                  if (compositeConverter == null) {
50                      addError("Failed to create converter for [%" + cn.getValue() + "] keyword");
51                      addToList(new LiteralConverter<E>("%PARSER_ERROR[" + cn.getValue() + "]"));
52                      break;
53                  }
54                  compositeConverter.setFormattingInfo(cn.getFormatInfo());
55                  compositeConverter.setOptionList(cn.getOptions());
56                  Compiler<E> childCompiler = new Compiler<E>(cn.getChildNode(), converterMap);
57                  childCompiler.setContext(context);
58                  Converter<E> childConverter = childCompiler.compile();
59                  compositeConverter.setChildConverter(childConverter);
60                  addToList(compositeConverter);
61                  break;
62              case Node.SIMPLE_KEYWORD:
63                  SimpleKeywordNode kn = (SimpleKeywordNode) n;
64                  DynamicConverter<E> dynaConverter = createConverter(kn);
65                  if (dynaConverter != null) {
66                      dynaConverter.setFormattingInfo(kn.getFormatInfo());
67                      dynaConverter.setOptionList(kn.getOptions());
68                      addToList(dynaConverter);
69                  } else {
70                      // if the appropriate dynaconverter cannot be found, then replace
71                      // it with a dummy LiteralConverter indicating an error.
72                      Converter<E> errConveter = new LiteralConverter<E>("%PARSER_ERROR[" + kn.getValue() + "]");
73                      addStatus(new ErrorStatus("[" + kn.getValue() + "] is not a valid conversion word", this));
74                      addToList(errConveter);
75                  }
76  
77              }
78          }
79          return head;
80      }
81  
82      private void addToList(Converter<E> c) {
83          if (head == null) {
84              head = tail = c;
85          } else {
86              tail.setNext(c);
87              tail = c;
88          }
89      }
90  
91      /**
92       * Attempt to create a converter using the information found in 'converterMap'.
93       *
94       * @param kn
95       * @return
96       */
97      @SuppressWarnings("unchecked")
98      DynamicConverter<E> createConverter(SimpleKeywordNode kn) {
99          String keyword = (String) kn.getValue();
100         Supplier<DynamicConverter> supplier = converterMap.get(keyword);
101 
102         if (supplier != null) {
103                 return supplier.get();
104         } else {
105             addError("There is no conversion supplier registered for conversion word [" + keyword + "]");
106             return null;
107         }
108     }
109 
110     /**
111      * Attempt to create a converter using the information found in
112      * 'compositeConverterMap'.
113      *
114      * @param cn
115      * @return
116      */
117     @SuppressWarnings("unchecked")
118     CompositeConverter<E> createCompositeConverter(CompositeNode cn) {
119         String keyword = (String) cn.getValue();
120         Supplier<DynamicConverter> supplier = (Supplier<DynamicConverter>) converterMap.get(keyword);
121 
122         if (supplier != null) {
123             try {
124                 return (CompositeConverter) supplier.get();
125             } catch(ClassCastException e) {
126                 addError("Failed to cast as CompositeConverter for keyword [" + keyword + "]", e);
127                 return null;
128             }
129         } else {
130             addError("There is no conversion class registered for composite conversion word [" + keyword + "]");
131             return null;
132         }
133     }
134 
135     // public void setStatusManager(StatusManager statusManager) {
136     // this.statusManager = statusManager;
137     // }
138     //
139     // void addStatus(Status status) {
140     // if(statusManager != null) {
141     // statusManager.add(status);
142     // }
143     // }
144 }