classes/hirondelle/web4j/model/AppResponseMessage.java
author Tomas Zeman <tzeman@volny.cz>
Wed, 04 Dec 2013 17:00:31 +0100
changeset 0 3060119b1292
permissions -rw-r--r--
Imported web4j 4.10.0
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
0
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     1
package hirondelle.web4j.model;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     2
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     3
import hirondelle.web4j.BuildImpl;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     4
import hirondelle.web4j.request.Formats;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     5
import hirondelle.web4j.request.RequestParameter;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     6
import hirondelle.web4j.ui.translate.Translator;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     7
import hirondelle.web4j.util.Args;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     8
import hirondelle.web4j.util.EscapeChars;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
     9
import hirondelle.web4j.util.Util;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    10
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    11
import java.io.IOException;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    12
import java.io.ObjectInputStream;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    13
import java.io.Serializable;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    14
import java.text.MessageFormat;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    15
import java.util.ArrayList;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    16
import java.util.Arrays;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    17
import java.util.Collections;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    18
import java.util.List;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    19
import java.util.Locale;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    20
import java.util.TimeZone;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    21
import java.util.logging.Logger;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    22
import java.util.regex.Matcher;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    23
import java.util.regex.Pattern;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    24
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    25
/**
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    26
 Informative message presented to the end user.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    27
   
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    28
 <P>This class exists in order to hide the difference between <em>simple</em> and 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    29
 <em>compound</em> messages. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    30
 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    31
 <P><a name="SimpleMessage"></a><b>Simple Messages</b><br>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    32
 Simple messages are a single {@link String}, such as <tt>'Item deleted successfully.'</tt>. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    33
 They are created using {@link #forSimple(String)}.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    34
 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    35
 <P><a name="CompoundMessage"></a><b>Compound Messages</b><br>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    36
 Compound messages are made up of several parts, and have parameters. They are created 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    37
 using {@link #forCompound(String, Object...)}. A compound message
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    38
 is usually implemented in Java using {@link java.text.MessageFormat}. <span class="highlight">
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    39
 However, <tt>MessageFormat</tt> is not used by this class, to avoid the following issues </span>:
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    40
<ul>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    41
 <li> the dreaded apostrophe problem. In <tt>MessageFormat</tt>, the apostrophe is a special 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    42
 character, and must be escaped. This is highly unnatural for translators, and has been a 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    43
 source of continual, bothersome errors. (This is the principal reason for not 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    44
 using <tt>MessageFormat</tt>.)
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    45
 <li>the <tt>{0}</tt> placeholders start at <tt>0</tt>, not <tt>1</tt>. Again, this is 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    46
 unnatural for translators.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    47
 <li>the number of parameters cannot exceed <tt>10</tt>. (Granted, it is not often 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    48
 that a large number of parameters are needed, but there is no reason why this 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    49
 restriction should exist.)
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    50
 <li>in general, {@link MessageFormat} is rather complicated in its details.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    51
</ul> 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    52
 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    53
 <P><a name="CustomFormat"></a><b>Format of Compound Messages</b><br>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    54
 This class defines an alternative format to that defined by {@link java.text.MessageFormat}. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    55
 For example,  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    56
 <PRE>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    57
  "At this restaurant, the _1_ meal costs _2_."
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    58
  "On _2_, I am going to Manon's place to see _1_."
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    59
 </PRE>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    60
 Here, 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    61
<ul>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    62
 <li>the placeholders appear as <tt>_1_</tt>, <tt>_2_</tt>, and so on. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    63
 They start at <tt>1</tt>, not <tt>0</tt>, and have no upper limit. There is no escaping 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    64
 mechanism to allow the placeholder text to appear in the message 'as is'. The <tt>_i_</tt>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    65
 placeholders stand for an <tt>Object</tt>, and carry no format information.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    66
 <li>apostrophes can appear anywhere, and do not need to be escaped.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    67
 <li>the formats applied to the various parameters are taken from {@link Formats}. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    68
 If the default formatting applied by {@link Formats} is not desired, then the caller 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    69
 can always manually format the parameter as a {@link String}. (The {@link Translator} may be used when 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    70
 a different pattern is needed for different Locales.)
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    71
 <li>the number of parameters passed at runtime must match exactly the number of <tt>_i_</tt> 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    72
 placeholders
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    73
</ul>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    74
 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    75
 <P><b>Multilingual Applications</b><br>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    76
 Multilingual applications will need to ensure that messages can be successfully translated when 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    77
 presented in JSPs. In particular, some care must be exercised to <em>not</em> create 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    78
 a <em>simple</em> message out of various pieces of data when a <em>compound</em> message  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    79
 should be used instead. See {@link #getMessage(Locale, TimeZone)}.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    80
 As well, see the <a href="../ui/translate/package-summary.html">hirondelle.web4j.ui.translate</a> 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    81
 package for more information, in particular the 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    82
 {@link hirondelle.web4j.ui.translate.Messages} tag used for rendering <tt>AppResponseMessage</tt>s, 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    83
 even in single language applications.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    84
 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    85
  <P><b>Serialization</b><br>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    86
  This class implements {@link Serializable} to allow messages stored in session scope to 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    87
  be transferred over a network, and thus survive a failover operation. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    88
  <i>However, this class's implementation of Serializable interface has a minor defect.</i>  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    89
  This class accepts <tt>Object</tt>s as parameters to messages. These objects almost always represent 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    90
  data - String, Integer, Id, DateTime, and so on, and all such building block classes are Serializable. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    91
  If, however, the caller passes an unusual message parameter object which is not Serializable, then the 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    92
  serialization of this object (if it occurs), will fail. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    93
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    94
  <P>The above defect will likely not be fixed since it has large ripple effects, and would seem to cause 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    95
  more problems than it would solve. In retrospect, this the message parameters passed to 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    96
  {@link #forCompound(String, Object[])} should likely have been typed as Serializable, not Object. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    97
*/
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    98
public final class AppResponseMessage implements Serializable {
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
    99
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   100
  /**
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   101
   <a href="#SimpleMessage">Simple message</a> having no parameters.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   102
   <tt>aSimpleText</tt> must have content.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   103
  */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   104
  public static AppResponseMessage forSimple(String aSimpleText){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   105
    return new AppResponseMessage(aSimpleText, NO_PARAMS);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   106
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   107
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   108
  /**
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   109
   <a href="#CompoundMessage">Compound message</a> having parameters.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   110
   
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   111
   <P><tt>aPattern</tt> follows the <a href="#CustomFormat">custom format</a> defined by this class.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   112
   {@link Formats#objectToTextForReport} will be used to format all parameters.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   113
    
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   114
   @param aPattern must be in the style of the <a href="#CustomFormat">custom format</a>, and 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   115
   the number of placeholders must match the number of items in <tt>aParams</tt>. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   116
   @param aParams must have at least one member; all members must be non-null, but may be empty 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   117
   {@link String}s.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   118
  */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   119
  public static AppResponseMessage forCompound(String aPattern, Object... aParams){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   120
    if ( aParams.length < 1 ){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   121
      throw new IllegalArgumentException("Compound messages must have at least one parameter.");
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   122
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   123
    return new AppResponseMessage(aPattern, aParams);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   124
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   125
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   126
  /**
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   127
   Return either the 'simple text' or the <em>formatted</em> pattern with all parameter data rendered, 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   128
   according to which factory method was called. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   129
   
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   130
   <P>The configured {@link Translator} is used to localize 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   131
   <ul>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   132
   <li>the text passed to {@link #forSimple(String)} 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   133
   <li>the pattern passed to {@link #forCompound(String, Object...)}
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   134
   <li>any {@link hirondelle.web4j.request.RequestParameter} parameters passed to {@link #forCompound(String, Object...)}
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   135
   are localized by using {@link Translator} on the return value of {@link RequestParameter#getName()} 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   136
   (This is intended for displaying localized versions of control names.) 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   137
   </ul>
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   138
    
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   139
   <P>It is highly recommended that this method be called <em>late</em> in processing, in a JSP.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   140
   
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   141
   <P>The <tt>Locale</tt> should almost always come from 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   142
   {@link hirondelle.web4j.BuildImpl#forLocaleSource()}.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   143
   The <tt>aLocale</tt> parameter is always required, even though there are cases when it 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   144
   is not actually used to render the result. 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   145
  */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   146
  public String getMessage(Locale aLocale, TimeZone aTimeZone){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   147
    String result = null;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   148
    Translator translator = BuildImpl.forTranslator();
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   149
    Formats formats = new Formats(aLocale, aTimeZone);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   150
    if( fParams.isEmpty() ){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   151
      result = translator.get(fText, aLocale);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   152
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   153
    else {
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   154
      String localizedPattern = translator.get(fText, aLocale);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   155
      List<String> formattedParams = new ArrayList<String>();
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   156
      for (Object param : fParams){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   157
        if ( param instanceof RequestParameter ){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   158
          RequestParameter reqParam = (RequestParameter)param;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   159
          String translatedParamName = translator.get(reqParam.getName(), aLocale); 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   160
          formattedParams.add( translatedParamName ); 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   161
        }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   162
        else {
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   163
          //this will escape any special HTML chars in params :
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   164
          formattedParams.add( formats.objectToTextForReport(param).toString() );
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   165
        }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   166
      }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   167
      result = populateParamsIntoCustomFormat(localizedPattern, formattedParams);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   168
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   169
    return result;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   170
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   171
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   172
  /**
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   173
   Return an unmodifiable <tt>List</tt> corresponding to the <tt>aParams</tt> passed to 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   174
   the constructor.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   175
   
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   176
   <P>If no parameters are being used, then return an empty list.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   177
  */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   178
  public List<Object> getParams(){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   179
    return Collections.unmodifiableList(fParams);  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   180
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   181
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   182
  /**
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   183
   Return either the 'simple text' or the pattern, according to which factory method 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   184
   was called. Typically, this method is <em>not</em> used to present text to the user (see {@link #getMessage}). 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   185
  */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   186
  @Override public String toString(){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   187
    return fText;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   188
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   189
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   190
  @Override public boolean equals(Object aThat){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   191
    Boolean result = ModelUtil.quickEquals(this, aThat);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   192
    if ( result == null ){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   193
      AppResponseMessage that = (AppResponseMessage) aThat;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   194
      result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   195
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   196
    return result;    
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   197
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   198
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   199
  @Override public int hashCode(){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   200
    return ModelUtil.hashCodeFor(getSignificantFields());
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   201
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   202
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   203
  // PRIVATE 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   204
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   205
  /** Holds either the simple text, or the custom pattern.  */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   206
  private final String fText;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   207
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   208
  /** List of Objects holds the parameters. Empty List if no parameters used.  */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   209
  private final List<Object> fParams;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   210
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   211
  private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("_(\\d)+_");
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   212
  private static final Object[] NO_PARAMS = new Object[0];
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   213
  private static final Logger fLogger = Util.getLogger(AppResponseMessage.class);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   214
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   215
  private static final long serialVersionUID = 1000L;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   216
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   217
  private AppResponseMessage(String aText, Object... aParams){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   218
    fText = aText;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   219
    fParams = Arrays.asList(aParams);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   220
    validateState();
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   221
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   222
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   223
  private void validateState(){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   224
    Args.checkForContent(fText);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   225
    if (fParams != null && fParams.size() > 0){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   226
      for(Object item : fParams){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   227
        if ( item == null ){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   228
          throw new IllegalArgumentException("Parameters to compound messages must be non-null.");
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   229
        }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   230
      }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   231
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   232
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   233
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   234
  /**
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   235
   @param aFormattedParams contains Strings ready to be placed in to the pattern. The index <tt>i</tt> of the 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   236
   List matches the <tt>_i_</tt> placeholder. The size of aFormattedParams must match the number of 
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   237
   placeholders.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   238
  */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   239
  private String populateParamsIntoCustomFormat(String aPattern, List<String> aFormattedParams){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   240
    StringBuffer result = new StringBuffer();
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   241
    fLogger.finest("Populating " + Util.quote(aPattern) + " with params " + Util.logOnePerLine(aFormattedParams));
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   242
    Matcher matcher = PLACEHOLDER_PATTERN.matcher(aPattern);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   243
    int numMatches = 0;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   244
    while ( matcher.find() ) {
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   245
      ++numMatches;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   246
      if(numMatches > aFormattedParams.size()){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   247
        String message = "The number of placeholders exceeds the number of available parameters (" + aFormattedParams.size() + ")";
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   248
        fLogger.severe(message);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   249
        throw new IllegalArgumentException(message);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   250
      }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   251
      matcher.appendReplacement(result, getReplacement(matcher, aFormattedParams));
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   252
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   253
    if(numMatches < aFormattedParams.size()){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   254
      String message = "The number of placeholders (" + numMatches + ") is less than the number of available parameters (" + aFormattedParams.size() + ")";
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   255
      fLogger.severe(message);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   256
      throw new IllegalArgumentException(message);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   257
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   258
    matcher.appendTail(result);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   259
    return result.toString();    
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   260
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   261
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   262
  private String getReplacement(Matcher aMatcher, List<String> aFormattedParams){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   263
    String result = null;
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   264
    String digit = aMatcher.group(1);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   265
    int idx = Integer.parseInt(digit);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   266
    if(idx <= 0){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   267
      throw new IllegalArgumentException("Placeholder digit should be 1,2,3... but takes value " + idx);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   268
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   269
    if(idx > aFormattedParams.size()){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   270
      throw new IllegalArgumentException("Placeholder index for _" + idx + "_ exceeds the number of available parameters (" + aFormattedParams.size() + ")");
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   271
    }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   272
    result = aFormattedParams.get(idx - 1);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   273
    return EscapeChars.forReplacementString(result);
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   274
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   275
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   276
  private Object[] getSignificantFields(){
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   277
    return new Object[] {fText, fParams};
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   278
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   279
  
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   280
   /**
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   281
    Always treat de-serialization as a full-blown constructor, by validating the final state of the deserialized object.
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   282
   */
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   283
   private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException {
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   284
     aInputStream.defaultReadObject();
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   285
     validateState();
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   286
  }
3060119b1292 Imported web4j 4.10.0
Tomas Zeman <tzeman@volny.cz>
parents:
diff changeset
   287
}