/[classpath]/cp-tools/src/gnu/localegen/CollationInterpreter.java
ViewVC logotype

Diff of /cp-tools/src/gnu/localegen/CollationInterpreter.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1 by dog, Wed Dec 22 20:59:22 2004 UTC revision 1.2 by cbj, Sun Jan 30 02:21:07 2005 UTC
# Line 9  import java.util.Iterator; Line 9  import java.util.Iterator;
9    
10  public class CollationInterpreter  public class CollationInterpreter
11  {  {
   static final int INVALID_OPERATOR = -1;  
   static final int PRIMARY = 0;  
   static final int SECONDARY = 1;  
   static final int TERTIARY = 2;  
   static final int IDENTICAL = 3;  
   static final int RESET = 4;  
   static final int EXPAND = 5;  
12    
   static final int IGNORABLE = 1;  
   static final int NON_IGNORABLE = 2;  
   static final int EXPANSION = 4;  
   static final int FIRST_RESET = 8;  
     
13    class CollationElement    class CollationElement
14    {    {
     int operator;  
15      String code;      String code;
     boolean multiChar;  
     int flags;  
16      CollationElement expand;      CollationElement expand;
17        int flags;
18        boolean multiChar;
19        int operator;
20      CollationElement reset;      CollationElement reset;
21    }    }
22      static final int EXPAND = 5;
23      static final int EXPANSION = 4;
24      static final int FIRST_RESET = 8;
25      static final int IDENTICAL = 3;
26      static final int IGNORABLE = 1;
27      static final int INVALID_OPERATOR = -1;
28      static final int NON_IGNORABLE = 2;
29      static final int PRIMARY = 0;
30      static final int RESET = 4;
31      static final int SECONDARY = 1;
32      static final int TERTIARY = 2;
33    
34      private static String fixForRuleBasedCollator(String s)
35      {
36        StringBuffer sbuf = null;
37        boolean useSBUF = false;
38        for (int i = 0; i < s.length(); i++)
39          {
40            char c = s.charAt(i);
41            if (!useSBUF)
42              {
43                if ((c >= 0x0009 && c <= 0x000d) || (c >= 0x0020 && c <= 0x002F)
44                    || (c >= 0x003A && c <= 0x0040) || (c >= 0x005B && c <= 0x0060)
45                    || (c >= 0x007B && c <= 0x007E))
46                  {
47                    useSBUF = true;
48                    sbuf = new StringBuffer();
49                    sbuf.append('\'');
50                    sbuf.append(s.substring(0, i + 1));
51                  }
52              }
53            else
54              {
55                if (s.charAt(i) == '\'')
56                  sbuf.append("''");
57                else
58                  sbuf.append(s.charAt(i));
59              }
60          }
61        if (useSBUF)
62          {
63            sbuf.append('\'');
64            return sbuf.toString();
65          }
66        else
67          return s;
68      }
69    
70      private static char getJavaOperator(int operator)
71      {
72        switch (operator)
73          {
74            case PRIMARY:
75              return '<';
76            case SECONDARY:
77              return ';';
78            case TERTIARY:
79              return ',';
80            case IDENTICAL:
81              return '=';
82            default:
83              throw new AssertionError("Invalid operator code " + operator);
84          }
85      }
86    
87    ArrayList collationData;    ArrayList collationData;
88    ArrayList expandedData = new ArrayList();    ArrayList expandedData = new ArrayList();
89      
90    public CollationInterpreter(ArrayList data)    public CollationInterpreter(ArrayList data)
91    {    {
92      this.collationData = data;      this.collationData = data;
93    }    }
94    
95      public void compute()
96      {
97        if (collationData == null)
98          return;
99        expandData();
100        reorderData();
101      }
102    
103    /*    /*
104     * This method fills the CollationElement with the correct type and     * This is the first pass. We translate the LDML rules and expand them a
105     * type information according to name. If it is unknown, the type is     * little further. The expansion involves pre-analysis of RESET rules and
106     * assigned to INVALID_OPERATOR.     * EXPAND rules. The output is an ordered array of collation rules with direct
107       * access to expansion rules.
108       */
109      void expandData()
110      {
111        Iterator collat = collationData.iterator();
112        int resetPosition;
113        int lastNonIgnorable = 0;
114        boolean newReset = true;
115        CollationElement resetElement;
116        resetElement = new CollationElement();
117        resetElement.code = "";
118        resetElement.flags |= FIRST_RESET;
119        resetPosition = 0;
120        while (collat.hasNext())
121          {
122            Element elt = (Element) collat.next();
123            CollationElement e = new CollationElement();
124            fillType(e, elt.qualifiedName);
125            e.reset = resetElement;
126            assert (e.operator != INVALID_OPERATOR);
127            switch (e.operator)
128              {
129                case EXPAND:
130                  {
131                    /*
132                     * Here we consider the expand type elements. They have a rule
133                     * order specification (p, s, t, pc, sc, tc, i, ic) and an
134                     * <extend> attribute. If we get two valid ones, we produce an
135                     * Error.
136                     *
137                     * Here we transform an EXPAND operator into a COLLATION
138                     * operator and mark the rule as to be expanded.
139                     */
140                    ExpansionElement expandElement = (ExpansionElement) elt;
141                    e.expand = new CollationElement();
142                    e.operator = INVALID_OPERATOR;
143                    e.flags |= EXPANSION;
144                    if (newReset)
145                      {
146                        e.flags |= FIRST_RESET;
147                        newReset = false;
148                      }
149                    e.expand.code = expandElement.extendData;
150                    fillType(e, expandElement.extendOperator);
151                    assert (e.operator != INVALID_OPERATOR);
152                    e.code = expandElement.extendOperatorData;
153                    expandedData.add(resetPosition, e);
154                    resetPosition++;
155                    if (lastNonIgnorable < resetPosition)
156                      lastNonIgnorable++;
157                    break;
158                  }
159                case RESET:
160                  {
161                    /*
162                     * RESET needs also a special treatment. In Java, we have only
163                     * automatic expansion and position reset. LDML may specific
164                     * logical position reset: - various ignoreable positions -
165                     * various non-ignoreable positions - normal symbol ordered
166                     * reset (after or before) In the first case, we will place all
167                     * elements as ignoreable and that's all for Java. The second
168                     * case needs a bit of logic.
169                     */
170                    ResetElement reset = (ResetElement) elt;
171                    switch (reset.logicalReset)
172                      {
173                        case ResetElement.FIRST_PRIMARY_IGNORABLE:
174                        case ResetElement.FIRST_SECONDARY_IGNORABLE:
175                        case ResetElement.FIRST_TERTIARY_IGNORABLE:
176                        case ResetElement.LAST_PRIMARY_IGNORABLE:
177                        case ResetElement.LAST_SECONDARY_IGNORABLE:
178                        case ResetElement.LAST_TERTIARY_IGNORABLE:
179                          e.flags |= IGNORABLE;
180                          resetPosition = 0; // All ignorables are in heading
181                                             // position in Java.
182                          break;
183                        case ResetElement.LAST_NON_IGNORABLE:
184                          resetPosition = lastNonIgnorable;
185                          break;
186                        default:
187                          {
188                            /*
189                             * We use in that case non-logical ordering We have to
190                             * do as in Java and parse the entire list to check
191                             * where to put the reset and the following elements.
192                             */
193                            assert (reset.data != null);
194                            assert (!reset.data.equals(""));
195                            resetElement.code = reset.data;
196                            for (int i = 0; i < expandedData.size(); i++)
197                              {
198                                CollationElement ce = (CollationElement) expandedData
199                                  .get(i);
200                                if (reset.data.equals(ce.code))
201                                  {
202                                    /*
203                                     * We have found it ! Now we may have to move to
204                                     * satisfy the "before" attribute.
205                                     */
206                                    // TODO TODO
207                                    resetPosition = i;
208                                  }
209                              }
210                            break;
211                          }
212                      }
213                    resetElement = e;
214                    break;
215                  }
216                default:
217                  /*
218                   * This is the default handler. If this is a sequence we expand
219                   * the list into single operation (e.multiChar == true). We mark
220                   * the first of the sequence as being a FIRST_RESET sequence if a
221                   * reset element has been issued.
222                   */
223                  {
224                    DataElement dat_elt = (DataElement) elt;
225                    if (e.multiChar)
226                      {
227                        // We have to completely expand the rule here.
228                        for (int i = 0; i < dat_elt.data.length(); i++)
229                          {
230                            CollationElement e2 = new CollationElement();
231                            e2.code = Character.toString(dat_elt.data.charAt(i));
232                            e2.flags = (e.flags | e.reset.flags) & ~FIRST_RESET;
233                            e2.reset = e.reset;
234                            e2.operator = e2.operator;
235                            if (newReset)
236                              {
237                                e2.flags |= FIRST_RESET;
238                                newReset = false;
239                              }
240                            expandedData.add(resetPosition, e2);
241                            resetPosition++;
242                          }
243                        if (lastNonIgnorable < resetPosition)
244                          lastNonIgnorable += dat_elt.data.length();
245                      }
246                    else
247                      {
248                        e.code = dat_elt.data;
249                        e.flags = (e.flags | e.reset.flags) & ~FIRST_RESET;
250                        expandedData.add(resetPosition, e);
251                        resetPosition++;
252                        if (newReset)
253                          {
254                            e.flags |= FIRST_RESET;
255                            newReset = false;
256                          }
257                      }
258                    if (lastNonIgnorable < resetPosition)
259                      lastNonIgnorable++;
260                  }
261                  break;
262              }
263          }
264        /*
265         * The collation data are now ready for reordering.
266         */
267        // Release the original array.
268        collationData = null;
269      }
270    
271      /*
272       * This method fills the CollationElement with the correct type and type
273       * information according to name. If it is unknown, the type is assigned to
274       * INVALID_OPERATOR.
275     */     */
276    void fillType(CollationElement e, String name)    void fillType(CollationElement e, String name)
277    {    {
278      int type;      int type;
279      boolean multiChar = false;      boolean multiChar = false;
   
280      if (name.equals("p"))      if (name.equals("p"))
281        {        {
282          type = PRIMARY;          type = PRIMARY;
283          multiChar = false;          multiChar = false;
284        }        }
285      else if (name.equals("s"))      else if (name.equals("s"))
286        {        {
287          type = SECONDARY;          type = SECONDARY;
288          multiChar = false;          multiChar = false;
289        }        }
290      else if (name.equals("t"))      else if (name.equals("t"))
291        {        {
292          type = TERTIARY;          type = TERTIARY;
293          multiChar = false;          multiChar = false;
294        }        }
295      else if (name.equals("i"))      else if (name.equals("i"))
296        {        {
297          type = IDENTICAL;          type = IDENTICAL;
298          multiChar = false;          multiChar = false;
299        }        }
300      else if (name.equals("pc"))      else if (name.equals("pc"))
301        {        {
302          type = PRIMARY;          type = PRIMARY;
303          multiChar = true;          multiChar = true;
304        }        }
305      else if (name.equals("sc"))      else if (name.equals("sc"))
306        {        {
307          type = SECONDARY;          type = SECONDARY;
308          multiChar = true;          multiChar = true;
309        }        }
310      else if (name.equals("tc"))      else if (name.equals("tc"))
311        {        {
312          type = TERTIARY;          type = TERTIARY;
313          multiChar = true;          multiChar = true;
314        }        }
315      else if (name.equals("ic"))      else if (name.equals("ic"))
316        {        {
317          type = IDENTICAL;          type = IDENTICAL;
318          multiChar = true;          multiChar = true;
319        }        }
320      else if (name.equals("reset"))      else if (name.equals("reset"))
321        {        {
322          type = RESET;          type = RESET;
323          multiChar = true;          multiChar = true;
324        }        }
325      else if (name.equals("x"))      else if (name.equals("x"))
326        {        {
327          type = EXPAND;          type = EXPAND;
328        }        }
329      else      else
330        type = INVALID_OPERATOR;        type = INVALID_OPERATOR;
   
331      e.operator = type;      e.operator = type;
332      e.multiChar = multiChar;      e.multiChar = multiChar;
333    }    }
334    
335    /*    /*
336     * This is the first pass. We translate the LDML rules and expand them a little further.     * expandData has taken care of placing all ignorable sequence at the begining
337     * The expansion involves pre-analysis of RESET rules and EXPAND rules.     * (for Java) and the rest after. All sequences are marked (IGNORABLE,
338     * The output is an ordered array of collation rules with direct access to expansion rules.     * NON_IGNORABLE or EXPAND). Here we will have to move EXPAND to the end and
339     */     * finish the complete expansion according to the context.
340    void expandData()     *
341    {     * At that stage may also occur CONTRACTION when a context element has been
342      Iterator collat = collationData.iterator();     * encountered.
     int resetPosition;  
     int lastNonIgnorable = 0;  
     boolean newReset = true;  
     CollationElement resetElement;  
   
     resetElement = new CollationElement();  
     resetElement.code = "";  
     resetElement.flags |= FIRST_RESET;  
     resetPosition = 0;  
       
     while (collat.hasNext())  
       {  
         Element elt = (Element)collat.next();  
         CollationElement e = new CollationElement();  
   
         fillType(e, elt.qualifiedName);  
           
         e.reset = resetElement;  
           
         assert (e.operator != INVALID_OPERATOR);  
   
         switch (e.operator) {  
         case EXPAND:  
           {  
             /*  
              * Here we consider the expand type elements.  
              * They have a rule order specification (p, s, t, pc, sc, tc, i, ic) and  
              * an <extend> attribute. If we get two valid ones, we produce an Error.  
              *  
              * Here we transform an EXPAND operator into a COLLATION operator and  
              * mark the rule as to be expanded.  
              */  
             ExpansionElement expandElement = (ExpansionElement)elt;  
               
             e.expand = new CollationElement();  
             e.operator = INVALID_OPERATOR;  
             e.flags |= EXPANSION;  
   
             if (newReset)  
               {  
                 e.flags |= FIRST_RESET;  
                 newReset = false;  
               }  
   
             e.expand.code = expandElement.extendData;  
             fillType(e, expandElement.extendOperator);  
             assert (e.operator != INVALID_OPERATOR);  
             e.code = expandElement.extendOperatorData;  
   
             expandedData.add(resetPosition, e);  
             resetPosition++;  
             if (lastNonIgnorable < resetPosition)  
               lastNonIgnorable++;  
             break;  
           }  
         case RESET:  
           {  
             /*  
              * RESET needs also a special treatment.  
              * In Java, we have only automatic expansion and position reset.  
              * LDML may specific logical position reset:  
              *   - various ignoreable positions  
              *   - various non-ignoreable positions  
              *   - normal symbol ordered reset (after or before)  
              * In the first case, we will place all elements as ignoreable  
              * and that's all for Java. The second case needs a bit of logic.  
              */  
             ResetElement reset = (ResetElement)elt;  
                       
             switch (reset.logicalReset)  
               {  
               case ResetElement.FIRST_PRIMARY_IGNORABLE:                  
               case ResetElement.FIRST_SECONDARY_IGNORABLE:  
               case ResetElement.FIRST_TERTIARY_IGNORABLE:  
               case ResetElement.LAST_PRIMARY_IGNORABLE:          
               case ResetElement.LAST_SECONDARY_IGNORABLE:  
               case ResetElement.LAST_TERTIARY_IGNORABLE:  
                 e.flags |= IGNORABLE;  
                 resetPosition = 0; // All ignorables are in heading position in Java.  
                 break;  
               case ResetElement.LAST_NON_IGNORABLE:  
                 resetPosition = lastNonIgnorable;  
                 break;  
               default:  
                 {  
                   /* We use in that case non-logical ordering  
                    * We have to do as in Java and parse the entire list  
                    * to check where to put the reset and the following elements.  
                    */  
                   assert (reset.data != null);  
                   assert (!reset.data.equals(""));  
   
                   resetElement.code = reset.data;  
                   for (int i = 0; i < expandedData.size(); i++)  
                     {  
                       CollationElement ce = (CollationElement)expandedData.get(i);  
   
                       if (reset.data.equals(ce.code))  
                         {  
                           /* We have found it !  
                            * Now we may have to move to satisfy the "before" attribute.  
                            */  
   
                           // TODO TODO  
                           resetPosition = i;  
                         }  
                     }  
                   break;  
                 }  
               }  
   
             resetElement = e;  
             break;  
           }  
   
         default:  
           /*  
            * This is the default handler. If this is a sequence we expand the list into  
            * single operation (e.multiChar == true). We mark the first of the sequence as  
            * being a FIRST_RESET sequence if a reset element has been issued.  
            */  
           {  
             DataElement dat_elt = (DataElement)elt;  
   
             if (e.multiChar)  
               {  
                 // We have to completely expand the rule here.  
                   
                 for (int i = 0; i < dat_elt.data.length(); i++)  
                   {  
                     CollationElement e2 = new CollationElement();  
                       
                     e2.code = Character.toString(dat_elt.data.charAt(i));  
                     e2.flags = (e.flags | e.reset.flags) & ~FIRST_RESET;  
                     e2.reset = e.reset;  
                     e2.operator = e2.operator;  
           
                     if (newReset)  
                       {  
                         e2.flags |= FIRST_RESET;  
                         newReset = false;  
                       }  
                     expandedData.add(resetPosition, e2);  
                     resetPosition++;  
                   }  
                 if (lastNonIgnorable < resetPosition)  
                   lastNonIgnorable += dat_elt.data.length();  
               }  
             else  
               {  
                 e.code = dat_elt.data;  
                 e.flags = (e.flags | e.reset.flags) & ~FIRST_RESET;  
                 expandedData.add(resetPosition, e);  
                 resetPosition++;  
   
                 if (newReset)  
                   {  
                     e.flags |= FIRST_RESET;  
                     newReset = false;  
                   }  
               }  
             if (lastNonIgnorable < resetPosition)  
               lastNonIgnorable++;  
           }  
           break;  
         }  
       }  
   
     /*  
      * The collation data are now ready for reordering.  
      */  
   
     // Release the original array.  
     collationData = null;  
   }  
   
   /*  
    * expandData has taken care of placing all ignorable sequence at the begining (for Java)  
    * and the rest after. All sequences are marked (IGNORABLE, NON_IGNORABLE or EXPAND).  
    * Here we will have to move EXPAND to the end and finish the complete expansion according  
    * to the context.  
    *  
    * At that stage may also occur CONTRACTION when a context element has been encountered.  
343     */     */
344    void reorderData()    void reorderData()
345    {    {
346      String sequence_context = null;      String sequence_context = null;
347      int originalSize = expandedData.size();      int originalSize = expandedData.size();
348      int i = 0;      int i = 0;
   
349      while (i < originalSize)      while (i < originalSize)
350        {        {
351          CollationElement ce = (CollationElement)expandedData.get(i);          CollationElement ce = (CollationElement) expandedData.get(i);
352            if ((ce.flags & EXPANSION) != 0 && (ce.flags & IGNORABLE) == 0)
353          if ((ce.flags & EXPANSION) != 0 && (ce.flags & IGNORABLE) == 0)            {
354            {              assert (sequence_context != null || (ce.reset.flags & FIRST_RESET) != 0);
355              assert (sequence_context != null || (ce.reset.flags & FIRST_RESET) != 0);              if ((ce.flags & FIRST_RESET) == 0)
356                              sequence_context = ce.reset.code;
357              if ((ce.flags & FIRST_RESET) == 0)              // We expand to remember the real position.
358                sequence_context = ce.reset.code;              ce.expand.code = sequence_context + ce.expand.code;
359                // Now we move it to the end.
360              // We expand to remember the real position.              expandedData.remove(i);
361              ce.expand.code = sequence_context + ce.expand.code;              originalSize--;
362              // Now we move it to the end.              expandedData.add(ce);
363              expandedData.remove(i);            }
364              originalSize--;          else
365              expandedData.add(ce);            {
366            }              sequence_context = ce.code;
367          else              i++;
368            {            }
             sequence_context = ce.code;  
             i++;  
           }  
       }  
   }  
   
   public void compute()  
   {  
     if (collationData == null)  
       return;  
       
     expandData();  
     reorderData();  
   }  
   
   private static String fixForRuleBasedCollator(String s)  
   {  
     StringBuffer sbuf = null;  
     boolean useSBUF = false;  
   
     for (int i = 0; i < s.length(); i++)  
       {  
         char c = s.charAt(i);  
   
         if (!useSBUF)  
           {  
             if ((c >= 0x0009 && c <= 0x000d) || (c >= 0x0020 && c <= 0x002F)  
                 || (c >= 0x003A && c <= 0x0040) || (c >= 0x005B && c <= 0x0060)  
                 || (c >= 0x007B && c <= 0x007E))  
             {  
               useSBUF = true;  
               sbuf = new StringBuffer();  
               sbuf.append('\'');  
               sbuf.append(s.substring(0, i+1));  
             }  
           }  
         else  
           {  
             if (s.charAt(i) == '\'')  
               sbuf.append("''");  
             else  
               sbuf.append(s.charAt(i));  
           }  
       }  
       
     if (useSBUF)  
       {  
         sbuf.append('\'');  
         return sbuf.toString();  
       }  
     else  
       return s;  
   }  
   
   private static char getJavaOperator(int operator)  
   {  
     switch (operator)  
       {  
       case PRIMARY:  
         return '<';  
       case SECONDARY:  
         return ';';  
       case TERTIARY:  
         return ',';  
       case IDENTICAL:  
         return '=';  
       default:  
         throw new AssertionError("Invalid operator code " + operator);  
369        }        }
370    }    }
371    
# Line 400  public class CollationInterpreter Line 374  public class CollationInterpreter
374      Iterator iter = expandedData.iterator();      Iterator iter = expandedData.iterator();
375      boolean ignoreFinished = false;      boolean ignoreFinished = false;
376      StringBuffer sb = new StringBuffer();      StringBuffer sb = new StringBuffer();
       
377      while (iter.hasNext())      while (iter.hasNext())
378        {        {
379          CollationElement ce = (CollationElement)iter.next();          CollationElement ce = (CollationElement) iter.next();
380            if ((ce.reset.flags & IGNORABLE) != 0)
381          if ((ce.reset.flags & IGNORABLE) != 0)            {
382            {              // We don't know how to put EXPANSION in IGNORABLE atm.
383              // We don't know how to put EXPANSION in IGNORABLE atm.              assert ((ce.flags & EXPANSION) == 0);
384              assert ((ce.flags & EXPANSION) == 0);              assert (!ignoreFinished);
385              assert (!ignoreFinished);              sb.append('=');
386                sb.append(fixForRuleBasedCollator(ce.code));
387              sb.append('=');            }
388              sb.append(fixForRuleBasedCollator(ce.code));          else if ((ce.flags & EXPANSION) != 0)
389            }            {
390          else if ((ce.flags & EXPANSION) != 0)              ignoreFinished = true;
391            {              sb.append('&');
392              ignoreFinished = true;              sb.append(fixForRuleBasedCollator(ce.expand.code));
393                sb.append(getJavaOperator(ce.operator));
394              sb.append('&');              sb.append(fixForRuleBasedCollator(ce.code));
395              sb.append(fixForRuleBasedCollator(ce.expand.code));            }
396              sb.append(getJavaOperator(ce.operator));          else
397              sb.append(fixForRuleBasedCollator(ce.code));            {
398            }              ignoreFinished = true;
399          else              sb.append(getJavaOperator(ce.operator));
400            {              sb.append(fixForRuleBasedCollator(ce.code));
401              ignoreFinished = true;            }
   
             sb.append(getJavaOperator(ce.operator));  
             sb.append(fixForRuleBasedCollator(ce.code));  
           }  
402        }        }
   
403      return sb.toString();      return sb.toString();
404    }    }
405  }  }

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26