f2190f0858a1f2d5b491bb59d8335122f5b1d30e
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / corext / template / TemplateTranslator.java
1 /*
2  * (c) Copyright IBM Corp. 2000, 2001.
3  * All Rights Reserved.
4  */
5 package net.sourceforge.phpdt.internal.corext.template;
6
7 import java.io.Reader;
8 import java.io.StringReader;
9 import java.util.HashMap;
10 import java.util.Iterator;
11 import java.util.Map;
12 import java.util.Set;
13 import java.util.Vector;
14
15 import org.eclipse.core.runtime.CoreException;
16
17 /**
18  * The template translator translates a string into a template buffer.
19  * The EBNF grammer of a valid string is as follows:
20  * 
21  * <p>
22  * template := (text | escape)*.<br />
23  * text := character - dollar.<br />
24  * escape := dollar ('{' identifier '}' | dollar).<br />
25  * dollar := '$'.<br />
26  * </p>
27  */
28 public class TemplateTranslator {
29
30         // states
31         private static final int TEXT= 0;
32         private static final int ESCAPE= 1;
33         private static final int IDENTIFIER= 2;
34
35         // tokens
36         private static final char ESCAPE_CHARACTER= '$';
37         private static final char IDENTIFIER_BEGIN= '{';
38         private static final char IDENTIFIER_END= '}';
39
40         /** a buffer for the translation result string */
41     private final StringBuffer fBuffer= new StringBuffer();    
42     /** position offsets of variables */
43     private final Vector fOffsets= new Vector();
44     /** position lengths of variables */
45     private final Vector fLengths= new Vector();
46
47         /** the current parsing state */
48     private int fState;    
49     /** the last translation error */
50     private String fErrorMessage;
51
52         /**
53          * Returns an error message if an error occured for the last translation, <code>null</code>
54          * otherwise.
55          */
56         public String getErrorMessage() {
57             return fErrorMessage;
58         }
59
60         /**
61          * Translates a template string to <code>TemplateBuffer</code>. <code>null</code>
62          * is returned if there was an error. <code>getErrorMessage()</code> retrieves the
63          * associated error message.
64          * 
65          * @param string the string to translate.
66          * @return returns the template buffer corresponding to the string, <code>null</code>
67          *         if there was an error.
68          * @see #getErrorMessage()
69          */
70         public TemplateBuffer translate(String string) throws CoreException {
71
72             fBuffer.setLength(0);
73             fOffsets.clear();
74             fLengths.clear();
75             fState= TEXT;
76             fErrorMessage= null;
77             
78                 if (!parse(string))
79                         return null;
80                         
81                 switch (fState) {
82                 case TEXT:
83                         break;
84                 
85                 // illegal, but be tolerant
86                 case ESCAPE:
87                         fErrorMessage= TemplateMessages.getString("TemplateTranslator.error.incomplete.variable"); //$NON-NLS-1$
88                         fBuffer.append(ESCAPE_CHARACTER);
89                         return null;
90                                 
91                 // illegal, but be tolerant
92                 case IDENTIFIER:
93                         fErrorMessage= TemplateMessages.getString("TemplateTranslator.error.incomplete.variable"); //$NON-NLS-1$
94                         fBuffer.append(ESCAPE_CHARACTER);
95                         return null;            
96                 }                       
97                 
98                 int[] offsets= new int[fOffsets.size()];
99                 int[] lengths= new int[fLengths.size()];
100                 
101                 for (int i= 0; i < fOffsets.size(); i++) {
102                         offsets[i]= ((Integer) fOffsets.get(i)).intValue();
103                         lengths[i]= ((Integer) fLengths.get(i)).intValue();
104                 }
105
106                 String translatedString= fBuffer.toString();
107                 TemplatePosition[] variables= findVariables(translatedString, offsets, lengths);
108
109                 return new TemplateBuffer(translatedString, variables);
110         }
111         
112         private static TemplatePosition[] findVariables(String string, int[] offsets, int[] lengths) {
113
114                 Map map= new HashMap();
115                 
116                 for (int i= 0; i != offsets.length; i++) {
117                     int offset= offsets[i];
118                     int length= lengths[i];
119                     
120                     String content= string.substring(offset, offset + length);
121                     Vector vector= (Vector) map.get(content);
122                     if (vector == null) {
123                         vector= new Vector();
124                         map.put(content, vector);
125                     }               
126                     vector.add(new Integer(offset));
127                 }
128                 
129                 TemplatePosition[] variables= new TemplatePosition[map.size()];
130                 int k= 0;
131                 
132                 Set keys= map.keySet();
133                 for (Iterator i= keys.iterator(); i.hasNext(); ) {
134                         String name= (String) i.next();                 
135                         Vector vector= (Vector) map.get(name);
136                         
137                         int[] offsets_= new int[vector.size()];
138                         for (int j= 0; j != offsets_.length; j++)
139                                 offsets_[j]= ((Integer) vector.get(j)).intValue();
140                                 
141                         variables[k]= new TemplatePosition(name, name, offsets_, name.length());
142                         k++;
143                 }
144                 
145                 return variables;
146         }
147
148         /** internal parser */
149         private boolean parse(String string) {
150
151                 for (int i= 0; i != string.length(); i++) {
152                     char ch= string.charAt(i);
153                         
154                         switch (fState) {
155                         case TEXT:
156                                 switch (ch) {
157                                 case ESCAPE_CHARACTER:
158                                         fState= ESCAPE;
159                                         break;
160                                         
161                                 default:
162                                         fBuffer.append(ch);
163                                         break;
164                                 }
165                                 break;
166                                 
167                         case ESCAPE:
168                                 switch (ch) {
169                                 case ESCAPE_CHARACTER:
170                                         fBuffer.append(ch);
171                                         fState= TEXT;
172                                         break;
173                                 
174                                 case IDENTIFIER_BEGIN:
175                                         fOffsets.add(new Integer(fBuffer.length()));
176                                         fState= IDENTIFIER;
177                                         break;
178                                         
179                                 default:
180                                         // illegal single escape character, but be tolerant
181                                         fErrorMessage= TemplateMessages.getString("TemplateTranslator.error.incomplete.variable"); //$NON-NLS-1$
182                                         fBuffer.append(ESCAPE_CHARACTER);
183                                         fBuffer.append(ch);
184                                         fState= TEXT;
185                                         return false;
186                                 }
187                                 break;
188
189                         case IDENTIFIER:
190                                 switch (ch) {
191                                 case IDENTIFIER_END:
192                                         int offset = ((Integer) fOffsets.get(fOffsets.size() - 1)).intValue();
193                                         fLengths.add(new Integer(fBuffer.length() - offset));
194                                         fState= TEXT;
195                                         break;
196                                 
197                                 default:
198                                         if (!Character.isUnicodeIdentifierStart((char) ch) &&
199                                                 !Character.isUnicodeIdentifierPart((char) ch))
200                                         {
201                                                 // illegal identifier character
202                                                 fErrorMessage= TemplateMessages.getString("TemplateTranslator.error.invalid.identifier"); //$NON-NLS-1$
203                                                 return false;
204                                         }
205                                 
206                                         fBuffer.append(ch);
207                                         break;
208                                 }
209                                 break;
210                         }
211                 }
212                 
213                 return true;
214         }
215
216 }