src/core/net/sf/basedb/util/formatter/PropertyFilterFormatter.java

Code
Comments
Other
Rev Date Author Line
6769 11 Mar 15 nicklas 1 /**
6769 11 Mar 15 nicklas 2   $Id$
6769 11 Mar 15 nicklas 3
6769 11 Mar 15 nicklas 4   Copyright (C) 2015 Nicklas Nordborg
6769 11 Mar 15 nicklas 5
6769 11 Mar 15 nicklas 6   This file is part of BASE - BioArray Software Environment.
6769 11 Mar 15 nicklas 7   Available at http://base.thep.lu.se/
6769 11 Mar 15 nicklas 8
6769 11 Mar 15 nicklas 9   BASE is free software; you can redistribute it and/or
6769 11 Mar 15 nicklas 10   modify it under the terms of the GNU General Public License
6769 11 Mar 15 nicklas 11   as published by the Free Software Foundation; either version 3
6769 11 Mar 15 nicklas 12   of the License, or (at your option) any later version.
6769 11 Mar 15 nicklas 13
6769 11 Mar 15 nicklas 14   BASE is distributed in the hope that it will be useful,
6769 11 Mar 15 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
6769 11 Mar 15 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
6769 11 Mar 15 nicklas 17   GNU General Public License for more details.
6769 11 Mar 15 nicklas 18
6769 11 Mar 15 nicklas 19   You should have received a copy of the GNU General Public License
6769 11 Mar 15 nicklas 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
6769 11 Mar 15 nicklas 21 */
6769 11 Mar 15 nicklas 22 package net.sf.basedb.util.formatter;
6769 11 Mar 15 nicklas 23
6769 11 Mar 15 nicklas 24 import java.util.Date;
6769 11 Mar 15 nicklas 25 import java.util.List;
6769 11 Mar 15 nicklas 26
6769 11 Mar 15 nicklas 27 import net.sf.basedb.core.DbControl;
6769 11 Mar 15 nicklas 28 import net.sf.basedb.core.Item;
7889 04 Dec 20 nicklas 29 import net.sf.basedb.core.ItemNotFoundException;
6769 11 Mar 15 nicklas 30 import net.sf.basedb.core.Nameable;
6769 11 Mar 15 nicklas 31 import net.sf.basedb.core.Operator;
6769 11 Mar 15 nicklas 32 import net.sf.basedb.core.PropertyFilter;
7889 04 Dec 20 nicklas 33 import net.sf.basedb.core.RawDataType;
7889 04 Dec 20 nicklas 34 import net.sf.basedb.core.RawDataTypes;
6769 11 Mar 15 nicklas 35 import net.sf.basedb.core.Type;
6769 11 Mar 15 nicklas 36 import net.sf.basedb.util.Values;
6769 11 Mar 15 nicklas 37
6769 11 Mar 15 nicklas 38 /**
6769 11 Mar 15 nicklas 39    Formatter for PropertyFilter items. This will create a more readable representation
6769 11 Mar 15 nicklas 40    of the property filter than the {@link PropertyFilter#toString()} method. It knows
6769 11 Mar 15 nicklas 41    about some special properties and load the names of annotation types, item subtypes, etc
6769 11 Mar 15 nicklas 42    instead of just displaying their ID values.
6769 11 Mar 15 nicklas 43    
6769 11 Mar 15 nicklas 44     @author Nicklas
6769 11 Mar 15 nicklas 45     @since 3.5
6769 11 Mar 15 nicklas 46  */
6769 11 Mar 15 nicklas 47 public class PropertyFilterFormatter 
6769 11 Mar 15 nicklas 48   implements Formatter<PropertyFilter>
6769 11 Mar 15 nicklas 49 {
6769 11 Mar 15 nicklas 50
6769 11 Mar 15 nicklas 51   private final DbControl dc;
6769 11 Mar 15 nicklas 52   private final Formatter<Date> dateFormatter;
7564 08 Jan 19 nicklas 53   private final boolean tagElements;
6769 11 Mar 15 nicklas 54   
6769 11 Mar 15 nicklas 55   /**
6769 11 Mar 15 nicklas 56     Create a new formatter.
6769 11 Mar 15 nicklas 57   */
6769 11 Mar 15 nicklas 58   public PropertyFilterFormatter(DbControl dc, Formatter<Date> dateFormatter)
6769 11 Mar 15 nicklas 59   {
7564 08 Jan 19 nicklas 60     this(dc, dateFormatter, false);
7564 08 Jan 19 nicklas 61   }
7564 08 Jan 19 nicklas 62   
7564 08 Jan 19 nicklas 63   /**
7564 08 Jan 19 nicklas 64     Create a new formatter with option to create HTML-like tags around elements. The
7564 08 Jan 19 nicklas 65     following tags are used: &lt;value&gt;, &lt;name&gt;, &lt;operator&gt;, &lt;function&gt;
7564 08 Jan 19 nicklas 66     @since 3.13.1
7564 08 Jan 19 nicklas 67   */
7564 08 Jan 19 nicklas 68   public PropertyFilterFormatter(DbControl dc, Formatter<Date> dateFormatter, boolean tagElements)
7564 08 Jan 19 nicklas 69   {
6769 11 Mar 15 nicklas 70     this.dc = dc;
6769 11 Mar 15 nicklas 71     this.dateFormatter = dateFormatter;
7564 08 Jan 19 nicklas 72     this.tagElements = tagElements;
6769 11 Mar 15 nicklas 73   }
6769 11 Mar 15 nicklas 74   
6769 11 Mar 15 nicklas 75   /*
6769 11 Mar 15 nicklas 76     From the Formatter interface
6769 11 Mar 15 nicklas 77     -------------------------------------------
6769 11 Mar 15 nicklas 78   */
6769 11 Mar 15 nicklas 79   @Override
6769 11 Mar 15 nicklas 80   public String format(PropertyFilter filter)
6769 11 Mar 15 nicklas 81   {    
6769 11 Mar 15 nicklas 82     String property = filter.getProperty();
6769 11 Mar 15 nicklas 83     Operator operator = filter.getOperator();
6769 11 Mar 15 nicklas 84     String value = filter.getValue();
6769 11 Mar 15 nicklas 85     String quote = filter.getValueType().isNumerical() || value == null ? "" : "'";
6769 11 Mar 15 nicklas 86     
6769 11 Mar 15 nicklas 87     StringBuilder sb = new StringBuilder();
7774 17 Feb 20 nicklas 88     
8083 20 Oct 22 nicklas 89     int numHops = 0;
8083 20 Oct 22 nicklas 90     while (property.startsWith("/") && numHops < 2)
7774 17 Feb 20 nicklas 91     {
7889 04 Dec 20 nicklas 92       // Parent or child item filter
8083 20 Oct 22 nicklas 93       numHops++;
7889 04 Dec 20 nicklas 94       String[] tmp = property.split("/", 5);
7889 04 Dec 20 nicklas 95       int baseIndex = 0;
7889 04 Dec 20 nicklas 96       String parentOrChild = "parent";
7889 04 Dec 20 nicklas 97       if (tmp.length == 5)
7889 04 Dec 20 nicklas 98       {
7889 04 Dec 20 nicklas 99         baseIndex = 1;
7889 04 Dec 20 nicklas 100         parentOrChild = tmp[1].toLowerCase();
7889 04 Dec 20 nicklas 101       }
7889 04 Dec 20 nicklas 102       
7889 04 Dec 20 nicklas 103       Item relatedItemType = Item.valueOf(tmp[baseIndex+1]);
7889 04 Dec 20 nicklas 104       property = tmp[baseIndex+3];
7889 04 Dec 20 nicklas 105       sb.append(function(parentOrChild)).append("(");
7889 04 Dec 20 nicklas 106       if (relatedItemType == Item.RAWBIOASSAY)
7889 04 Dec 20 nicklas 107       {
7889 04 Dec 20 nicklas 108         RawDataType rdt = RawDataTypes.getRawDataType(tmp[baseIndex+2]);
7889 04 Dec 20 nicklas 109         if (rdt == null)
7889 04 Dec 20 nicklas 110         {
7889 04 Dec 20 nicklas 111           sb.append(value(missing(tmp[baseIndex+2], "This raw data type doesn't exist!")));
7889 04 Dec 20 nicklas 112         }
7889 04 Dec 20 nicklas 113         else
7889 04 Dec 20 nicklas 114         {
7889 04 Dec 20 nicklas 115           sb.append(value(rdt.getName()));
7889 04 Dec 20 nicklas 116         }
7889 04 Dec 20 nicklas 117       }
7889 04 Dec 20 nicklas 118       else
7889 04 Dec 20 nicklas 119       {
7889 04 Dec 20 nicklas 120         int subtypeId = Values.getInt(tmp[baseIndex+2]);
7889 04 Dec 20 nicklas 121         sb.append(value(nameOfItem(dc, Item.ITEMSUBTYPE, subtypeId, false)));
7889 04 Dec 20 nicklas 122       }
7774 17 Feb 20 nicklas 123       sb.append(")");
7774 17 Feb 20 nicklas 124       if (!property.startsWith("#")) sb.append(".");
7774 17 Feb 20 nicklas 125     }
8083 20 Oct 22 nicklas 126     if (property.startsWith("|"))
7889 04 Dec 20 nicklas 127     {
7889 04 Dec 20 nicklas 128       // Linked item filter
7889 04 Dec 20 nicklas 129       String[] tmp = property.split("\\|", 4);
7889 04 Dec 20 nicklas 130       sb.append(function("link")).append("(");
7889 04 Dec 20 nicklas 131       sb.append(value(tmp[1]));
7889 04 Dec 20 nicklas 132       if (tmp.length > 2)
7889 04 Dec 20 nicklas 133       {
7889 04 Dec 20 nicklas 134         sb.append(", ").append(value(tmp[2]));
7889 04 Dec 20 nicklas 135       }
7889 04 Dec 20 nicklas 136       sb.append(")");
7889 04 Dec 20 nicklas 137       if (tmp.length > 3)
7889 04 Dec 20 nicklas 138       {
7889 04 Dec 20 nicklas 139         sb.append(".");
7889 04 Dec 20 nicklas 140         property = tmp[3];
7889 04 Dec 20 nicklas 141       }
7889 04 Dec 20 nicklas 142       else
7889 04 Dec 20 nicklas 143       {
7889 04 Dec 20 nicklas 144         property = "";
7889 04 Dec 20 nicklas 145       }
7889 04 Dec 20 nicklas 146     }
7774 17 Feb 20 nicklas 147     
6769 11 Mar 15 nicklas 148     if (property.startsWith("#"))
6769 11 Mar 15 nicklas 149     {
6769 11 Mar 15 nicklas 150       // Annotation type filter
7559 07 Jan 19 nicklas 151       if (property.contains("("))
7559 07 Jan 19 nicklas 152       {
7559 07 Jan 19 nicklas 153         // The annotation is on a joined property: #(path.to.join)id
7559 07 Jan 19 nicklas 154         int joinStart = property.indexOf('(');
7559 07 Jan 19 nicklas 155         int joinEnd = property.indexOf(')');
7565 08 Jan 19 nicklas 156         sb.append(name(property.substring(joinStart+1, joinEnd)));
7559 07 Jan 19 nicklas 157         property = property.substring(0, joinStart) + property.substring(joinEnd+1); // Should give us: #id
7559 07 Jan 19 nicklas 158       }
7559 07 Jan 19 nicklas 159       
6769 11 Mar 15 nicklas 160       int startIndex = property.startsWith("##") ? 2 : 1;
6769 11 Mar 15 nicklas 161       int annotationTypeId = Values.getInt(property.substring(startIndex));
7564 08 Jan 19 nicklas 162       sb.append("{").append(name(nameOfItem(dc, Item.ANNOTATIONTYPE, annotationTypeId, false))).append("}");
7564 08 Jan 19 nicklas 163       sb.append(" ").append(operator(operator.toString())).append(" ");
6769 11 Mar 15 nicklas 164       if (operator.isListOperator() && value != null)
6769 11 Mar 15 nicklas 165       {
6769 11 Mar 15 nicklas 166         appendListOfValues(sb, value, quote);
6769 11 Mar 15 nicklas 167       }
6769 11 Mar 15 nicklas 168       else
6769 11 Mar 15 nicklas 169       {
7564 08 Jan 19 nicklas 170         sb.append(value(quote+value+quote));
6769 11 Mar 15 nicklas 171       }
6769 11 Mar 15 nicklas 172     }
6841 09 Apr 15 nicklas 173     else if (property.startsWith("¤"))
6841 09 Apr 15 nicklas 174     {
6841 09 Apr 15 nicklas 175       // Data file type filter
6841 09 Apr 15 nicklas 176       int i = property.indexOf('(');
6841 09 Apr 15 nicklas 177       int fileTypeId = Values.getInt(property.substring(1, i));
6841 09 Apr 15 nicklas 178       String fileFilter = property.substring(i+1, property.length()-1);
7564 08 Jan 19 nicklas 179       sb.append("{").append(name(nameOfItem(dc, Item.DATAFILETYPE, fileTypeId, false)+"#"+fileFilter)).append("}");
7564 08 Jan 19 nicklas 180       sb.append(" ").append(operator(operator.toString())).append(" ");
6841 09 Apr 15 nicklas 181       if (operator.isListOperator() && value != null)
6841 09 Apr 15 nicklas 182       {
6841 09 Apr 15 nicklas 183         appendListOfValues(sb, value, quote);
6841 09 Apr 15 nicklas 184       }
6841 09 Apr 15 nicklas 185       else
6841 09 Apr 15 nicklas 186       {
7564 08 Jan 19 nicklas 187         sb.append(value(quote+value+quote));
6841 09 Apr 15 nicklas 188       }
6841 09 Apr 15 nicklas 189     }
6769 11 Mar 15 nicklas 190     else if (property.startsWith("§"))
6769 11 Mar 15 nicklas 191     {
6769 11 Mar 15 nicklas 192       // Item list filter
7889 04 Dec 20 nicklas 193       if (operator.isNegationOperator()) sb.append(operator("NOT "));
7889 04 Dec 20 nicklas 194       sb.append(function("memberOfList")).append("(");      
7889 04 Dec 20 nicklas 195       List<Object> listIds = filter.getValuesAsObjects();
7889 04 Dec 20 nicklas 196       String sep = "";
7889 04 Dec 20 nicklas 197       for (Object listId : listIds)
7889 04 Dec 20 nicklas 198       {
7889 04 Dec 20 nicklas 199         sb.append(sep);
7889 04 Dec 20 nicklas 200         sep = ", ";
7889 04 Dec 20 nicklas 201         sb.append(value(nameOfItem(dc, Item.ITEMLIST, (Integer)listId, true)));
7889 04 Dec 20 nicklas 202       }
6769 11 Mar 15 nicklas 203       sb.append(")");
6769 11 Mar 15 nicklas 204     }
7494 04 Jun 18 nicklas 205     else if (property.equals("bioWell.row") || property.equals("bioWell.column"))
6769 11 Mar 15 nicklas 206     {
7494 04 Jun 18 nicklas 207       WellCoordinateFormatter formatter = new WellCoordinateFormatter(property.equals("bioWell.row"));
7564 08 Jan 19 nicklas 208       sb.append(name(property)).append(" ").append(operator(operator.toString())).append(" ");
7494 04 Jun 18 nicklas 209       List<Object> selected = filter.getValuesAsObjects();
7494 04 Jun 18 nicklas 210       if (selected.size() > 1) sb.append("(");
7494 04 Jun 18 nicklas 211       if (selected.size() == 0) sb.append("null");
6769 11 Mar 15 nicklas 212       String sep = "";
7494 04 Jun 18 nicklas 213       for (Object index : selected)
6769 11 Mar 15 nicklas 214       {
6769 11 Mar 15 nicklas 215         sb.append(sep);
6769 11 Mar 15 nicklas 216         sep = ", ";
7564 08 Jan 19 nicklas 217         sb.append(value(formatter.format((Integer)index)));
6769 11 Mar 15 nicklas 218       }
7494 04 Jun 18 nicklas 219       if (selected.size() > 1) sb.append(")");
6769 11 Mar 15 nicklas 220     }
7494 04 Jun 18 nicklas 221     else if (property.equals("parentType"))
7494 04 Jun 18 nicklas 222     {
7564 08 Jan 19 nicklas 223       sb.append(name(property)).append(" ").append(operator(operator.toString())).append(" ");
7494 04 Jun 18 nicklas 224       List<Object> selected = filter.getValuesAsObjects();
7494 04 Jun 18 nicklas 225       if (selected.size() > 1) sb.append("(");
7494 04 Jun 18 nicklas 226       if (selected.size() == 0) sb.append("null");
7494 04 Jun 18 nicklas 227       String sep = "";
7494 04 Jun 18 nicklas 228       for (Object index : selected)
7494 04 Jun 18 nicklas 229       {
7494 04 Jun 18 nicklas 230         sb.append(sep);
7494 04 Jun 18 nicklas 231         sep = ", ";
7564 08 Jan 19 nicklas 232         sb.append(value(Item.fromValue((Integer)index).toString()));
7494 04 Jun 18 nicklas 233       }
7494 04 Jun 18 nicklas 234       if (selected.size() > 1) sb.append(")");
7494 04 Jun 18 nicklas 235     }
6769 11 Mar 15 nicklas 236     else
6769 11 Mar 15 nicklas 237     {
7494 04 Jun 18 nicklas 238       Item nameOfItemType = null;
7494 04 Jun 18 nicklas 239       if (property.equals("itemSubtype") || property.endsWith(".itemSubtype"))
6769 11 Mar 15 nicklas 240       {
7494 04 Jun 18 nicklas 241         // Special handling to display name of subtype
7494 04 Jun 18 nicklas 242         nameOfItemType = Item.ITEMSUBTYPE;
6769 11 Mar 15 nicklas 243       }
7494 04 Jun 18 nicklas 244       else if (property.equals("platform"))
6769 11 Mar 15 nicklas 245       {
7494 04 Jun 18 nicklas 246         // Special handling to display name of platform
7494 04 Jun 18 nicklas 247         nameOfItemType = Item.PLATFORM;
6769 11 Mar 15 nicklas 248       }
7494 04 Jun 18 nicklas 249       if (nameOfItemType != null)
7494 04 Jun 18 nicklas 250       {
7564 08 Jan 19 nicklas 251         sb.append(name(property)).append(" ").append(operator(operator.toString())).append(" ");
7494 04 Jun 18 nicklas 252         List<Object> itemIds = filter.getValuesAsObjects();
7494 04 Jun 18 nicklas 253         if (itemIds.size() > 1) sb.append("(");
7494 04 Jun 18 nicklas 254         if (itemIds.size() == 0) sb.append("null");
7494 04 Jun 18 nicklas 255         String sep = "";
7494 04 Jun 18 nicklas 256         for (Object id : itemIds)
7494 04 Jun 18 nicklas 257         {
7494 04 Jun 18 nicklas 258           sb.append(sep);
7494 04 Jun 18 nicklas 259           sep = ", ";
7564 08 Jan 19 nicklas 260           sb.append(value(nameOfItem(dc, nameOfItemType, (Integer)id, true)));
7494 04 Jun 18 nicklas 261         }
7494 04 Jun 18 nicklas 262         if (itemIds.size() > 1) sb.append(")");
7494 04 Jun 18 nicklas 263       }
6769 11 Mar 15 nicklas 264       else
6769 11 Mar 15 nicklas 265       {
8083 20 Oct 22 nicklas 266         if (property.startsWith("&") || property.startsWith("@")) 
6769 11 Mar 15 nicklas 267         {
7494 04 Jun 18 nicklas 268           property = property.substring(1);
6769 11 Mar 15 nicklas 269         }
8083 20 Oct 22 nicklas 270         else if (property.startsWith("!x."))
8083 20 Oct 22 nicklas 271         {
8083 20 Oct 22 nicklas 272           property = property.substring(3);
8083 20 Oct 22 nicklas 273         }
7564 08 Jan 19 nicklas 274         sb.append(name(property)).append(" ").append(operator(operator.toString())).append(" ");
7494 04 Jun 18 nicklas 275         if (operator.isListOperator() && value != null)
7494 04 Jun 18 nicklas 276         {
7494 04 Jun 18 nicklas 277           appendListOfValues(sb, value, quote);
7494 04 Jun 18 nicklas 278         }
7494 04 Jun 18 nicklas 279         else
7494 04 Jun 18 nicklas 280         {
7494 04 Jun 18 nicklas 281           if (filter.getValueType().isTemporal())
7494 04 Jun 18 nicklas 282           {
7494 04 Jun 18 nicklas 283             value = dateFormatter.format((Date)Type.DATE.parseString(value));
7494 04 Jun 18 nicklas 284           }
7564 08 Jan 19 nicklas 285           sb.append(value(quote+value+quote));
7494 04 Jun 18 nicklas 286         }
6769 11 Mar 15 nicklas 287       }
6769 11 Mar 15 nicklas 288     }
6769 11 Mar 15 nicklas 289     return sb.toString();
6769 11 Mar 15 nicklas 290   }
6769 11 Mar 15 nicklas 291
6769 11 Mar 15 nicklas 292   /**
6769 11 Mar 15 nicklas 293     @throws UnsupportedOperationException Always
6769 11 Mar 15 nicklas 294   */
6769 11 Mar 15 nicklas 295   @Override
6769 11 Mar 15 nicklas 296   public PropertyFilter parseString(String value)
6769 11 Mar 15 nicklas 297   {
6769 11 Mar 15 nicklas 298     throw new UnsupportedOperationException("parseString: " + value);
6769 11 Mar 15 nicklas 299   }
6769 11 Mar 15 nicklas 300   // -------------------------------------------
6769 11 Mar 15 nicklas 301
6769 11 Mar 15 nicklas 302   
6769 11 Mar 15 nicklas 303   private String nameOfItem(DbControl dc, Item itemType, int id, boolean quote)
6769 11 Mar 15 nicklas 304   {
6769 11 Mar 15 nicklas 305     String q = quote ? "'" : "";
6769 11 Mar 15 nicklas 306     try
6769 11 Mar 15 nicklas 307     {
6769 11 Mar 15 nicklas 308       return q+((Nameable)itemType.getById(dc, id)).getName()+q;
6769 11 Mar 15 nicklas 309     }
7889 04 Dec 20 nicklas 310     catch (ItemNotFoundException ex)
7889 04 Dec 20 nicklas 311     {
7889 04 Dec 20 nicklas 312       return missing(Integer.toString(id), "This " + itemType.toString().toLowerCase() + " doesn't exist!");
7889 04 Dec 20 nicklas 313     }
6769 11 Mar 15 nicklas 314     catch (RuntimeException ex)
6769 11 Mar 15 nicklas 315     {
7889 04 Dec 20 nicklas 316       return missing(Integer.toString(id), ex.getMessage());
6769 11 Mar 15 nicklas 317     }
6769 11 Mar 15 nicklas 318   }
6769 11 Mar 15 nicklas 319   
6769 11 Mar 15 nicklas 320   private void appendListOfValues(StringBuilder sb, String value, String quote)
6769 11 Mar 15 nicklas 321   {
6769 11 Mar 15 nicklas 322     String[] values = value.split("\\|");
6769 11 Mar 15 nicklas 323     if (values.length > 1) sb.append("(");
6769 11 Mar 15 nicklas 324     String sep = "";
6769 11 Mar 15 nicklas 325     for (Object v : values)
6769 11 Mar 15 nicklas 326     {
6769 11 Mar 15 nicklas 327       sb.append(sep);
6769 11 Mar 15 nicklas 328       sep = ", ";
7564 08 Jan 19 nicklas 329       sb.append(value(quote+v+quote));
6769 11 Mar 15 nicklas 330     }
6769 11 Mar 15 nicklas 331     if (values.length > 1) sb.append(")");
6769 11 Mar 15 nicklas 332     
6769 11 Mar 15 nicklas 333   }
7564 08 Jan 19 nicklas 334   
7564 08 Jan 19 nicklas 335   private String value(String value)
7564 08 Jan 19 nicklas 336   {
7564 08 Jan 19 nicklas 337     return tagElements ? "<value>"+value+"</value>" : value;
7564 08 Jan 19 nicklas 338   }
7564 08 Jan 19 nicklas 339   
7564 08 Jan 19 nicklas 340   private String name(String name)
7564 08 Jan 19 nicklas 341   {
7564 08 Jan 19 nicklas 342     return tagElements ? "<name>"+name+"</name>" : name;
7564 08 Jan 19 nicklas 343   }
7564 08 Jan 19 nicklas 344   private String operator(String op)
7564 08 Jan 19 nicklas 345   {
7564 08 Jan 19 nicklas 346     return tagElements ? "<operator>"+op+"</operator>" : op;
7564 08 Jan 19 nicklas 347   }
7564 08 Jan 19 nicklas 348   private String function(String function)
7564 08 Jan 19 nicklas 349   {
7564 08 Jan 19 nicklas 350     return tagElements ? "<function>"+function+"</function>" : function;
7564 08 Jan 19 nicklas 351   }
7889 04 Dec 20 nicklas 352   private String missing(String missing, String title)
7889 04 Dec 20 nicklas 353   {
7889 04 Dec 20 nicklas 354     return tagElements ? "<missing title=\""+(title!=null ? title :"Missing")+"\">"+missing+"</missing>" : missing;
7889 04 Dec 20 nicklas 355   }
7564 08 Jan 19 nicklas 356
7564 08 Jan 19 nicklas 357   
6769 11 Mar 15 nicklas 358 }