View Javadoc
1   package emissary.util;
2   
3   import emissary.config.ConfigUtil;
4   import emissary.config.Configurator;
5   import emissary.core.Family;
6   import emissary.core.IBaseDataObject;
7   import emissary.core.TransformHistory;
8   import emissary.util.xml.JDOMUtil;
9   
10  import org.jdom2.Document;
11  import org.jdom2.Element;
12  import org.slf4j.Logger;
13  import org.slf4j.LoggerFactory;
14  
15  import java.io.IOException;
16  import java.time.Instant;
17  import java.util.Collection;
18  import java.util.HashMap;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.regex.Pattern;
23  
24  /**
25   * Utilities for dealing with IBaseDataObject and Lists thereof
26   */
27  public class PayloadUtil {
28      public static final Logger logger = LoggerFactory.getLogger(PayloadUtil.class);
29  
30      private static final String LS = System.getProperty("line.separator");
31      private static final Pattern validFormRegex = Pattern.compile("^[\\w-)(/+]+$");
32  
33      protected static final Map<String, String> historyPreference = new HashMap<>();
34  
35      protected static final String REDUCED_HISTORY = "REDUCED_HISTORY";
36      protected static final String NO_URL = "NO_URL";
37  
38      @SuppressWarnings("NonFinalStaticField")
39      protected static boolean compactHistory;
40  
41      static {
42          configure();
43      }
44  
45      protected static void configure() {
46          Configurator configG = null;
47          try {
48              configG = ConfigUtil.getConfigInfo(PayloadUtil.class);
49              compactHistory = configG.findBooleanEntry("COMPACT_HISTORY", false);
50          } catch (IOException e) {
51              logger.error("Cannot open default config file", e);
52          }
53  
54          // fill lists with data from PayloadUtil.cfg for transform itinerary/history
55          if (configG != null) {
56              for (String fileType : configG.findEntries(REDUCED_HISTORY)) {
57                  setFileTypeHistoryPreference(fileType, REDUCED_HISTORY);
58              }
59              for (String fileType : configG.findEntries(NO_URL)) {
60                  setFileTypeHistoryPreference(fileType, NO_URL);
61              }
62          }
63      }
64  
65      /**
66       * Check if fileType is already mapped to history preference. If not, set fileType -$gt; preference in map
67       *
68       * @param fileType current fileType pulled from cfg
69       * @param preference preference associated with current cfg fileType
70       */
71      protected static void setFileTypeHistoryPreference(String fileType, String preference) {
72          if (historyPreference.containsKey(fileType)) {
73              if (historyPreference.get(fileType).equals(preference)) {
74                  // log if filetype is assigned in the same preference more than once
75                  logger.warn("FileType {} is assigned to {} in cfg more than once.", fileType, preference);
76              } else {
77                  // log if filetype already has previous preference assignment
78                  logger.warn("FileType {} already has history preference {} assigned. {} will be ignored.", fileType,
79                          historyPreference.get(fileType), preference);
80              }
81          } else {
82              historyPreference.put(fileType, preference);
83          }
84      }
85  
86      /**
87       * Try really hard to get a meaningful name for a payload object
88       */
89      public static String getName(final Object o) {
90          String payloadName = o.getClass().getName();
91          if (o instanceof IBaseDataObject) {
92              payloadName = ((IBaseDataObject) o).shortName();
93          } else if (o instanceof Collection) {
94              final Iterator<?> pi = ((Collection<?>) o).iterator();
95              if (pi.hasNext()) {
96                  payloadName = ((IBaseDataObject) pi.next()).shortName() + "(" + ((Collection<?>) o).size() + ")";
97              }
98          }
99          return payloadName;
100     }
101 
102     /**
103      * Generate a string about the payload object
104      * 
105      * @param payload the payload to describe
106      * @param oneLine true for a condensed one-line string
107      */
108     public static String getPayloadDisplayString(final IBaseDataObject payload, final boolean oneLine) {
109         return oneLine ? getPayloadOneLineString(payload) : getPayloadDisplayString(payload);
110     }
111 
112     /**
113      * Generate a string about the payload object
114      * 
115      * @param payload the payload to describe
116      */
117     public static String getPayloadDisplayString(final IBaseDataObject payload) {
118         final StringBuilder sb = new StringBuilder();
119         final TransformHistory th = payload.getTransformHistory();
120         final String fileName = payload.getFilename();
121         final String fileType = payload.getFileType();
122         final List<String> currentForms = payload.getAllCurrentForms();
123         final Instant creationTimestamp = payload.getCreationTimestamp();
124 
125         sb.append("\n").append("filename: ").append(fileName).append("\n").append("   creationTimestamp: ").append(creationTimestamp).append("\n")
126                 .append("   currentForms: ").append(currentForms).append("\n").append("   filetype: ").append(fileType).append("\n")
127                 .append("   transform history (").append(th.size(true)).append(") :").append("\n");
128 
129         // transform history output
130         String historyCase = configureHistoryCase(fileType);
131         if (historyCase.equals(REDUCED_HISTORY)) {
132             // found reduced history match, output only dropoff
133             sb.append("   ** reduced transform history **").append("\n").append("     dropOff -> ")
134                     .append(payload.getLastPlaceVisited())
135                     .append("\n");
136         } else if (compactHistory) {
137             for (String history : th.format()) {
138                 sb.append("     ").append(history).append("\n");
139             }
140         } else {
141             for (final TransformHistory.History h : th.getHistory()) {
142                 sb.append("     ").append(h.getKey(historyCase.equals(NO_URL))).append("\n");
143                 for (final String coord : h.getCoordinated(historyCase.equals(NO_URL))) {
144                     sb.append("      ").append(coord).append("\n");
145                 }
146             }
147         }
148 
149         return sb.toString();
150     }
151 
152     /**
153      * Check if fileType in PayloadUtil.cfg matches current payload fileType
154      *
155      * @param fileType current payload fileType
156      * @return string for output case
157      */
158     public static String configureHistoryCase(String fileType) {
159         if (historyPreference.containsKey(fileType)) {
160             return historyPreference.get(fileType);
161         }
162         // no match for current fileType, return empty string
163         return "";
164     }
165 
166     /**
167      * Generate a one-line string about the payload object
168      * 
169      * @param payload the payload to describe
170      */
171     public static String getPayloadOneLineString(final IBaseDataObject payload) {
172         final StringBuilder sb = new StringBuilder();
173         final String fn = payload.getFilename();
174         final int attPos = fn.indexOf(Family.SEP);
175         if (attPos != -1) {
176             sb.append(fn.substring(attPos + 1)).append(" ");
177         }
178         final List<String> th = payload.transformHistory(true);
179         String prev = "";
180         for (final String h : th) {
181             final int pos = h.indexOf(".");
182             if (pos > 0) {
183                 final String prefix = h.substring(0, pos);
184                 if (!prev.equals(prefix)) {
185                     if (prev.length() != 0) {
186                         sb.append(",");
187                     }
188                     sb.append(prefix);
189                     prev = prefix;
190                 }
191             }
192         }
193         sb.append(">>").append(payload.getAllCurrentForms());
194         sb.append("//").append(payload.getFileType());
195         sb.append("//").append(payload.getCreationTimestamp());
196         return sb.toString();
197     }
198 
199     /**
200      * Turn the payload into an xml jdom document
201      * 
202      * @param d the payload
203      */
204     public static Document toXml(final IBaseDataObject d) {
205         final Element root = new Element("payload");
206         root.addContent(JDOMUtil.protectedElement("name", d.getFilename()));
207         final Element cf = new Element("current-forms");
208         for (final String c : d.getAllCurrentForms()) {
209             cf.addContent(JDOMUtil.simpleElement("current-form", c));
210         }
211         root.addContent(cf);
212         root.addContent(JDOMUtil.simpleElement("encoding", d.getFontEncoding()));
213         root.addContent(JDOMUtil.simpleElement("filetype", d.getFileType()));
214         root.addContent(JDOMUtil.simpleElement("classification", d.getClassification()));
215         final Element th = new Element("transform-history");
216         for (final String s : d.transformHistory(true)) {
217             th.addContent(JDOMUtil.simpleElement("itinerary-step", s));
218         }
219         root.addContent(th);
220         if (d.getProcessingError() != null) {
221             root.addContent(JDOMUtil.simpleElement("processing-error", d.getProcessingError()));
222         }
223         final Element meta = new Element("metadata");
224         for (final String key : d.getParameters().keySet()) {
225             final Element m = JDOMUtil.protectedElement("param", d.getStringParameter(key));
226             m.setAttribute("name", key);
227             meta.addContent(m);
228         }
229         root.addContent(meta);
230 
231         if (d.header() != null) {
232             root.addContent(JDOMUtil.protectedElement("header", d.header()));
233         }
234         if (d.dataLength() > 0) {
235             root.addContent(JDOMUtil.protectedElement("data", d.data()));
236         }
237         if (d.footer() != null) {
238             root.addContent(JDOMUtil.protectedElement("footer", d.footer()));
239         }
240 
241         // Alt views
242         if (d.getNumAlternateViews() > 0) {
243             final Element views = new Element("views");
244             for (final String av : d.getAlternateViewNames()) {
245                 final Element v = JDOMUtil.protectedElement("view", d.getAlternateView(av));
246                 v.setAttribute("name", av);
247                 views.addContent(v);
248             }
249             root.addContent(views);
250         }
251 
252         logger.debug("Produced xml document for {}", d.shortName());
253         return new Document(root);
254     }
255 
256     /**
257      * Turn a list of payload into an xml jdom ocument
258      * 
259      * @param list the payload list
260      */
261     public static Document toXml(final List<IBaseDataObject> list) {
262         final Element root = new Element("payload-list");
263         for (final IBaseDataObject d : list) {
264             final Document doc = toXml(d);
265             root.addContent(doc.detachRootElement());
266             logger.debug("Adding xml content for {} to document", d.shortName());
267         }
268         return new Document(root);
269     }
270 
271     /**
272      * Turn the payload into an xml string
273      * 
274      * @param d the payload
275      */
276     public static String toXmlString(final IBaseDataObject d) {
277         return JDOMUtil.toString(toXml(d));
278     }
279 
280     /**
281      * Turn the payload list into an xml string
282      * 
283      * @param list the payload list
284      */
285     public static String toXmlString(final List<IBaseDataObject> list) {
286         return JDOMUtil.toString(toXml(list));
287     }
288 
289     /**
290      * Print formatted metadata key:value pairs
291      */
292     private static final String SEP = ": ";
293 
294     public static String printFormattedMetadata(final IBaseDataObject payload) {
295         final StringBuilder out = new StringBuilder();
296         out.append(LS);
297         for (final Map.Entry<String, Collection<Object>> entry : payload.getParameters().entrySet()) {
298             out.append(entry.getKey()).append(SEP).append(entry.getValue()).append(LS);
299         }
300         return out.toString();
301     }
302 
303     /**
304      * Checks whether the form complies with form rules established by a regex
305      *
306      * Approved forms can contain alpha-numerics, '-', '_', '()', '/', '+'
307      *
308      * @param form The form to be tested
309      * @return Whether the form is Emissary compliant
310      */
311     public static boolean isValidForm(String form) {
312         return validFormRegex.matcher(form).find();
313     }
314 
315     /** This class is not meant to be instantiated. */
316     private PayloadUtil() {}
317 }