1    package com.instantbank.component.lettertemplate.util;
2    
3    import java.util.ArrayList;
4    
5    import java.io.Serializable;
6    import com.instantbank.common.utilcomponents.LetterTemplateGlobals;
7    import com.instantbank.lettertemplate.control.util.JSPUtil;
8    import com.instantbank.lettertemplate.control.LetterTemplateEventException;
9    
10   import com.instantbank.lettertemplate.editor.web.TemplateTransformer;
11   
12   /**
13    *  In memory representation of a letter component.
14    *
15    * @author InstantBank (Rodrigo Lopez)
16    * @created September 2002
17    */
18   public class LetterComponent
19       implements Serializable, LetterViewable {
20   
21     /**
22      *  Identifies the "header type" of the component.
23      */
24     public static final int HEADER = 1;
25   
26     /**
27      *  Identifies the "body type" of the component.
28      */
29     public static final int BODY = 2;
30   
31     /**
32      *  Identifies the "closing type" of the component.
33      */
34     public static final int CLOSING = 3;
35   
36     /**
37      *  Identifies the image attribute for a character.
38      */
39     public static final String IMGTYPE = "I";
40   
41     /**
42      *  Identifies the variable attribute for a character.
43      */
44     public static final String VARTYPE = "V";
45   
46     /**
47      *  Indicates if this component is up to date with respect to the database.
48      *  True if it is out of date, False if it is up to date.
49      */
50     public boolean hasChanged = false;
51   
52     /**
53      *  Component type : HEADER, BODY, CLOSING
54      */
55     private int type;
56   
57     /**
58      *  Component print type: LASER, TYPEWRITTER
59      */
60     private int printType;
61   
62     /**
63      *  Unique code in the data base.
64      */
65     private long code;
66   
67     /**
68      *  Component description.
69      */
70     private String name;
71   
72     /**
73      *  ArrayList containing < position,image> pairs. "Position" is stored as
74      *  an Integer objet and "image" is stored as a byte[] object. They are in
75      *  descending order, by position.
76      */
77     private ArrayList traceImages;
78   
79     /**
80      *  ArrayList containing < position,fieldId, formatCode, offset> tuple. "Position"
81      *  is stored as an Integer objet "fieldId", "formatCode" and "offset" are stored as
82      *  Long objects. They are in descending order, by position.
83      */
84     private ArrayList traceVariables;
85   
86     /**
87      *  The text contained in the template in RTF format. Positions of the images
88      *  and variables are not directly related to this text (see {@link
89      *  #traceImages} and {@link #traceVariables}). Those positions are related to
90      *  the document resulting from this text after it is read by the
91      *  corresponding swing component.
92      */
93     private String rtfText;
94   
95     /**
96      *  Time stamp of the last time the component was saved. It is used as
97      *  "version Id" for the component.
98      */
99     private String stamp;
100  
101  
102    /**
103     *  Simple constructor from {@link #type} and {@link #printType}. Moreover,
104     *  {@link #code}, {@link #name}, and {@link #stamp} are initialized to
105     *  "undefined".
106     *
107     * @param type The component type: {@link #BODY}, {@link #HEADER},
108     *      {@link #CLOSING}.
109     * @param printType The component print type: LASER, TYPEWITTER.
110     */
111    public LetterComponent(int type, int printType) {
112      this.type = type;
113      this.printType = printType;
114      this.code = LetterTemplateGlobals.UNDEF;
115      this.name = LetterTemplateGlobals.STR_UNNAMED;
116      this.stamp = LetterTemplateGlobals.STR_UNDEF;
117      this.hasChanged = false;
118    }
119  
120  
121    /**
122     *  Big constructor. Defines all attributes from parameters.
123     *
124     * @param code Component's code.
125     * @param type Component's type: HEADER, BODY, CLOSING.
126     * @param printType Component's print type: LASER, TYPEWRITTER.
127     * @param name Component's name.
128     * @param rtfText Component's rtf text.
129     * @param traceVariables Variables in the component.
130     * @param traceImages Images in the component.
131     * @param stamp Time stamp of the last time the component was saved.
132     */
133    public LetterComponent(long code, int type, int printType, String name,
134                           String rtfText, ArrayList traceVariables, ArrayList traceImages,
135                           String stamp) {
136      this.code = code;
137      this.type = type;
138      this.printType = printType;
139      this.name = name;
140      this.rtfText = rtfText;
141      this.traceVariables = traceVariables;
142      this.traceImages = traceImages;
143      this.stamp = stamp;
144    }
145  
146  
147    /**
148     *  Getter method for {@link #type}
149     *
150     * @return The type value
151     */
152    public int getType() {
153      return (this.type);
154    }
155  
156  
157    /**
158     *  Setter method for {@link #type}
159     *
160     * @param type The new type value
161     */
162    public void setType(int type) {
163      this.type = type;
164    }
165  
166  
167    /**
168     *  Getter method for {@link #printType}
169     *
170     * @return The printType value
171     */
172    public int getPrintType() {
173      return (this.printType);
174    }
175  
176  
177    /**
178     *  Setter method for {@link #printType}
179     *
180     * @param printType The new printType value
181     */
182    public void setPrintType(int printType) {
183      this.printType = printType;
184    }
185  
186  
187    /**
188     *  Getter method for {@link #code}
189     *
190     * @return The code value
191     */
192    public long getCode() {
193      return this.code;
194    }
195  
196  
197    /**
198     *  Setter method for {@link #code}
199     *
200     * @param code The new code value
201     */
202    public void setCode(long code) {
203      this.code = code;
204    }
205  
206  
207    /**
208     *  Getter method for {@link #name}
209     *
210     * @return The name value
211     */
212    public String getName() {
213      return (this.name);
214    }
215  
216  
217    /**
218     *  Setter method for {@link #name}
219     *
220     * @param name The new name value
221     */
222    public void setName(String name) {
223      this.name = name;
224    }
225  
226  
227    /**
228     *  Getter method for {@link #traceImages}
229     *
230     * @return The traceImages value
231     */
232    public ArrayList getTraceImages() {
233      return (this.traceImages);
234    }
235  
236  
237    /**
238     *  Setter method for {@link #traceImages}
239     *
240     * @param traceImages The new traceImages value
241     */
242    public void setTraceImages(ArrayList traceImages) {
243      this.traceImages = traceImages;
244    }
245  
246  
247    /**
248     *  Getter method for {@link #traceVariables}
249     *
250     * @return The traceVariables value
251     */
252    public ArrayList getTraceVariables() {
253      return (this.traceVariables);
254    }
255  
256  
257    /**
258     *  Setter method for {@link #traceVariables}
259     *
260     * @param traceVariables The new traceVariables value
261     */
262    public void setTraceVariables(ArrayList traceVariables) {
263      this.traceVariables = traceVariables;
264    }
265  
266  
267    /**
268     *  Getter method for {@link #rtfText}
269     *
270     * @return The rtfText value
271     */
272    public String getRtfText() {
273      return rtfText;
274    }
275  
276  
277    /**
278     *  Setter method for {@link #rtfText}
279     *
280     * @param rtfText The new rtfText value
281     */
282    public void setRtfText(String rtfText) {
283      this.rtfText = rtfText;
284    }
285  
286  
287    /**
288     *  Getter method for {@link #stamp}
289     *
290     * @return The stamp value
291     */
292    public String getStamp() {
293      return stamp;
294    }
295  
296  
297    /**
298     *  Setter method for {@link #stamp}
299     *
300     * @param st The new stamp value
301     */
302    public void setStamp(String st) {
303      stamp = st;
304    }
305  
306  
307    /**
308     *  Produces a human readable/printable version of the code, name and stamp
309     *  attributes of this LetterComponent.
310     *
311     * @return String representing codes
312     */
313    public String codesToString() {
314      return "<" + code + " , " + name + "," + stamp + ">";
315    }
316  
317  
318    /**
319     *  Produces a human readable version of a component type code.
320     *
321     * @param type The component type code.
322     * @return String representing component type cod
323     */
324    public static String typeToString(int type) {
325      switch (type) {
326        case HEADER:
327          return "header";
328        case BODY:
329          return "body";
330        case CLOSING:
331          return "closing";
332        default:
333          return "unknown";
334      }
335    }
336  
337  
338    /**
339     *  Produces a human readable version of the plural of a component type code.
340     *
341     * @param type The component type code.
342     * @return String representing the plural of the component type code
343     */
344    public static String typeToPlural(int type) {
345      switch (type) {
346        case HEADER:
347          return "headers";
348        case BODY:
349          return "bodies";
350        case CLOSING:
351          return "closings";
352        default:
353          return "unknown";
354      }
355    }
356  
357  
358    /**
359     *  Produces a human readable version of the plural of a component type code.
360     *
361     * @param type The component type name.
362     * @return String representing the plural of the component type code
363     */
364    public static String typeToPlural(String type) {
365      String base = type.toLowerCase();
366      if(base.equals("header")) {
367        return "headers";
368      }
369      else if(base.equals("body")) {
370        return "bodies";
371      }
372      else {
373        return "closings";
374      }
375    }
376  
377  
378    /**
379     *  Calculates the "merge" of this component's variables and images, in
380     *  descending order by their position in the text.
381     *
382     * @return An array of ArrayLists. Each entry is an ArrayList of size=2:
383     *
384     *      <ul>
385     *        <li> The first position contains the "type" of the second position,
386     *        coded as "V" = variable, "I"=image.
387     *        <li> The second position contains an ArrayList coming from {@link
388     *        #traceVariables} or {@link #traceImages}, according to the type.
389     *
390     *      </ul>
391     */
392    public ArrayList[] mergeVarsImages() {
393      int mergeSize = traceImages.size() + traceVariables.size();
394      ArrayList[] merge = new ArrayList[mergeSize];
395      ArrayList slot = null;
396      ArrayList mSlot = null;
397      int v = 0;
398      int i = 0;
399      int m = 0;
400  
401      while(m < mergeSize) {
402        if(i >= traceImages.size()) {
403          //No images left
404          while(v < traceVariables.size()) {
405            //Place remaining variables
406            slot = (ArrayList)traceVariables.get(v);
407            mSlot = new ArrayList(2);
408            mSlot.add(VARTYPE);
409            mSlot.add(slot);
410            merge[m] = mSlot;
411            m++;
412            v++;
413          }
414        }
415        else if(v >= traceVariables.size()) {
416          //No variables left
417          while(i < traceImages.size()) {
418            //Place remaining images
419            slot = (ArrayList)traceImages.get(i);
420            mSlot = new ArrayList(2);
421            mSlot.add(IMGTYPE);
422            mSlot.add(slot);
423            merge[m] = mSlot;
424            m++;
425            i++;
426          }
427        }
428        else {
429          int posVar;
430          int posImg;
431          ArrayList slotVar;
432          ArrayList slotImg;
433          slotVar = (ArrayList)traceVariables.get(v);
434          posVar = ((Integer)slotVar.get(0)).intValue();
435          slotImg = (ArrayList)traceImages.get(i);
436          posImg = ((Integer)slotImg.get(0)).intValue();
437  
438          if(posVar > posImg) {
439            //Place variable
440            mSlot = new ArrayList(2);
441            mSlot.add(VARTYPE);
442            mSlot.add(slotVar);
443            merge[m] = mSlot;
444            m++;
445            v++;
446          }
447          else {
448            //Place image
449            mSlot = new ArrayList(2);
450            mSlot.add(IMGTYPE);
451            mSlot.add(slotImg);
452            merge[m] = mSlot;
453            m++;
454            i++;
455          }
456        }
457      }
458  
459      return merge;
460    }
461  
462  
463    /**
464     *  Delivers a String representation of a merge structure for variables and
465     *  images.
466     *
467     * @param merge The merge structure.
468     * @return String representing merge structure for variables and images.
469     */
470    public String mergeToString(ArrayList[] merge) {
471      ArrayList slot;
472      String line;
473      String resp = "VarImg Merge:\n";
474      int pos;
475      long varCode;
476      for(int i = 0; i < merge.length; i++) {
477        slot = merge[i];
478        String type = (String)slot.get(0);
479        slot = (ArrayList)slot.get(1);
480        pos = ((Integer)slot.get(0)).intValue();
481        line = "[ " + type + " pos= " + pos;
482        if(type.equals("V")) {
483          varCode = ((Long)slot.get(1)).longValue();
484          line += " varCode= " + varCode;
485        }
486        line += " ]";
487        resp += line + "\n";
488      }
489      return resp;
490    }
491  
492  
493    /**
494     *  traceVarsToString
495     *
496     * @return String
497     */
498    public String traceVarsToString() {
499      return traceVarsToString(traceVariables);
500    }
501  
502  
503    /**
504     *  traceVarsToString
505     *
506     * @param trace
507     * @return String
508     */
509    public String traceVarsToString(ArrayList trace) {
510      String resp = typeToString(type) + " " + name + " Variables trace: \n";
511      String line;
512      for(int i = 0; i < trace.size(); i++) {
513        ArrayList slot = (ArrayList)trace.get(i);
514        Integer pos = (Integer)slot.get(0);
515        Long varCode = (Long)slot.get(1);
516        line = "[ pos =" + pos + " , varCode= " + varCode + "]";
517        resp += line + "\n";
518      }
519  
520      return resp;
521    }
522  
523  
524    /**
525     *  traceImgToStringod
526     *
527     * @return String
528     */
529    public String traceImgToString() {
530      return traceImgToString(traceImages);
531    }
532  
533  
534    /**
535     *  traceImgToString
536     *
537     * @param trace
538     * @return String
539     */
540    public String traceImgToString(ArrayList trace) {
541      String resp = "Images trace: \n";
542      String line;
543      for(int i = 0; i < trace.size(); i++) {
544        ArrayList slot = (ArrayList)trace.get(i);
545        Integer pos = (Integer)slot.get(0);
546        line = "[ pos =" + pos + " ]";
547        resp += line + "\n";
548      }
549      return resp;
550    }
551  
552  
553    /**
554     *  Verifies if the trace is in descending order --by variable or image
555     *  position. Can be used on a variable trace as well as on an images trace.
556     *
557     * @param trace
558     * @return boolean
559     */
560    public boolean wellFormed(ArrayList trace) {
561      if(trace.size() == 0) {
562        return true;
563      }
564  
565      ArrayList slot = (ArrayList)trace.get(0);
566      int pre = ((Integer)slot.get(0)).intValue();
567      for(int i = 1; i < trace.size(); i++) {
568        slot = (ArrayList)trace.get(i);
569        int post = ((Integer)slot.get(0)).intValue();
570        if(post >= pre) {
571          return false;
572        }
573        pre = post;
574      }
575      return true;
576    }
577  
578  
579    /**
580     *  Verifies if the merge is in descending order --by variable or image
581     *  position--.
582     *
583     * @param merge A structure resulting from {@link #mergeVarsImages()}.
584     * @return boolean
585     */
586    public boolean wellFormed(ArrayList[] merge) {
587      if(merge.length == 0) {
588        return true;
589      }
590  
591      ArrayList slot = (ArrayList)((ArrayList)merge[0]).get(1);
592      int pre = ((Integer)slot.get(0)).intValue();
593      for(int i = 1; i < merge.length; i++) {
594        slot = (ArrayList)((ArrayList)merge[i]).get(1);
595        int post = ((Integer)slot.get(0)).intValue();
596        if(post >= pre) {
597          return false;
598        }
599        pre = post;
600      }
601      return true;
602    }
603  
604  
605    /**
606     * Delivers an array with variables' information, in ascending order by
607     * position.
608     *
609     * @return An array of < position,fieldId, formatCode>
610     */
611    public ArrayList[] getAllVariables() {
612      ArrayList[] res = new ArrayList[traceVariables.size()];
613  
614      int i = 0;
615  
616      int j;
617  
618      j = traceVariables.size() - 1;
619      while(j >= 0) {
620        res[i++] = (ArrayList)traceVariables.get(j--);
621      }
622      return res;
623    }
624  
625  
626    /**
627     * Delivers an array with images' information, in ascending order by
628     * position:
629     *
630     * @return An Array of < position,image>
631     */
632    public ArrayList[] getAllImages() {
633      ArrayList[] res = new ArrayList[traceImages.size()];
634  
635      int i = 0;
636  
637      int j;
638  
639      j = traceImages.size() - 1;
640      while(j >= 0) {
641        res[i++] = (ArrayList)traceImages.get(j--);
642      }
643      return res;
644    }
645  
646  
647    /**
648     * Fixes the rtf text of this component. Due to a bug in swing's RTFEditorKit,
649     * here we must
650     * substitute the "\fnil" font family by actual font families. The current
651     * families in the system are "\fmodern, \froman and \fswiss". The
652     * substitution is guided by the equivalences established by FONT constants in
653     * class LetterOp. The central part of the algorithm replaces ocurrences of a
654     * fontable like <pre>
655     * {\fonttbl\f0\fnil Monospaced;\f1\fnil Times New Roman;\f2\fnil Arial;}
656     * </pre> by something with actual font families like: <pre>
657     * {\fonttbl\f0\fmodern Courier;\f1\froman Times;\f2\fswiss helvetica;}
658     * </pre> <p>
659     *
660     * @return The fixed rtf text .
661     */
662    public String extractRtf() {
663      StringBuffer res = new StringBuffer(rtfText);
664  
665      //Replaces this component's font table
666      int startFontTable = 0;
667      int endFontTable = 0;
668      String fontTable = "";
669  
670      startFontTable = rtfText.indexOf("\\fonttbl");
671      endFontTable = rtfText.indexOf("}", startFontTable);
672  
673      fontTable = rtfText.substring(startFontTable, endFontTable);
674      res.replace(startFontTable, endFontTable,
675        TemplateTransformer.actualFontTable(fontTable));
676  
677      return res.toString();
678    }
679  
680  
681    /**
682     * Answers whether or not this component has variables.
683     *
684     * @return True if it has variables, False otherwise.
685     */
686    public boolean hasVariables() {
687      return traceVariables != null && traceVariables.size() > 0;
688    }
689  
690  
691    /**
692     * Transform this LetterComponent in a "fo-template" without external references to
693     * image files.
694     *
695     * @return The fo-template --as a String-- or null if something goes wrong.
696     */
697    public String toFoTemplate() {
698      String rtf = extractRtf();
699  
700      //Substitutes variables occurrences in the rtfText by the
701      //rtf variable convention.
702      try {
703        if(hasVariables()) {
704          rtf = TemplateTransformer.substituteVariableTags(rtf, getAllVariables());
705        }
706      }
707      catch(LetterTemplateEventException lex) {
708        //Must never happen if 'rtf' is produced by the system
709      }
710  
711      //Produces the fo-template version of the text.
712      return TemplateTransformer.rtftoFoTemplate(rtf);
713    }
714  
715  
716    /**
717     *  Calculates the set --without repetitions-- of codes of variables mentioned
718     *  in this components. Each variable will be paired with
719     *  a list of [formats, dateOffset].
720     *
721     * @return Array of [varCode, [(format1, offset1), (format2,offset2), ..]].
722     *  If there is no
723     *      variables, returns an array with 0 entries.
724     */
725    public ArrayList[] setOfVariables() {
726      return TemplateTransformer.setOfVariables(traceVariables);
727    }
728  }
729  
730