source: code/Website/open-flash-chart/br/com/stimuli/string/printf.as

Last change on this file was 7849, checked in by dennisw, 15 years ago
File size: 11.0 KB
Line 
1package br.com.stimuli.string{
2
3 /**
4 * Creates a string with variable substitutions. Very similiar to printf, specially python's printf
5 * @param raw The string to be substituted.
6 * @param rest The objects to be substitued, can be positional or by properties inside the object (in wich case only one object can be passed)
7 * @return The formated and substitued string.
8 * @example
9 * <pre>
10 * import br.com.stimuli.string.printf;
11 * // objects are substitued in the other they appear
12 *
13 * printf("This is an %s lybrary for creating %s", "Actioscript 3.0", "strings");
14 * // outputs: "This is an Actioscript 3.0 lybrary for creating strings";
15 * // you can also format numbers:
16 *
17 * printf("You can also display numbers like PI: %f, and format them to a fixed precision, such as PI with 3 decimal places %.3f", Math.PI, Math.PI);
18 * // outputs: " You can also display numbers like PI: 3.141592653589793, and format them to a fixed precision, such as PI with 3 decimal places 3.142"
19 * // Instead of positional (the order of arguments to print f, you can also use propertie of an object):
20 * var userInfo : Object = {
21 "name": "Arthur Debert",
22 "email": "arthur@stimuli.com.br",
23 "website":"http://www.stimuli.com.br/",
24 "ocupation": "developer"
25 }
26 *
27 * printf("My name is %(name)s and I am a %(ocupation)s. You can read more on my personal %(website)s, or reach me through my %(email)s", userInfo);
28 * // outputs: "My name is Arthur Debert and I am a developer. You can read more on my personal http://www.stimuli.com.br/, or reach me through my arthur@stimuli.com.br"
29 * // you can also use date parts:
30 * var date : Date = new Date();
31 * printf("Today is %d/%m/%Y", date, date, date)
32 *
33 * </pre>
34 * @see br.com.stimuli.string
35 */
36 public function printf(raw : String, ...rest) : String{
37 /**
38 * Pretty ugly!
39 * basicaly
40 * % -> the start of a substitution hole
41 * (some_var_name) -> [optional] used in named substitutions
42 * .xx -> [optional] the precision with witch numbers will be formated
43 * x -> the formatter (string, hexa, float, date part)
44 */
45 var SUBS_RE : RegExp = /%(\((?P<var_name>[\w_\d]+)\))?(\.(?P<precision>[0-9]))?(?P<formater>[sxofaAbBcdHIjmMpSUwWxXyYZ])/ig;
46
47 var matches : Array = [];
48 var result : Object = SUBS_RE.exec(raw);
49 var match : Match;
50 var runs : int = 0;
51 var numMatches : int = 0;
52 var numberVariables : int = rest.length;
53 // quick check if we find string subs amongst the text to match (something like %(foo)s
54 var isPositionalSubts : Boolean = !Boolean(raw.match(/%\(\s*[\w\d_]+\s*\)/));
55 trace(raw, isPositionalSubts);
56 var replacementValue : *;
57 var formater : String;
58 var varName : String;
59 var precision : String;
60 // matched through the string, creating Match objects for easier later reuse
61 while (Boolean(result)){
62 match = new Match();
63 match.startIndex = result.index;
64 match.length = String(result[0]).length;
65 match.endIndex = match.startIndex + match.length;
66 match.content = String(result[0]);
67 trace(match.content);
68 // try to get substitution properties
69 formater = result.formater;
70 varName = result.var_name;
71 precision = result.precision;
72
73 if (isPositionalSubts){
74 // by position, grab next subs:
75 try{
76 replacementValue = rest[matches.length];
77 }catch(e : Error){
78 throw new Error(BAD_VARIABLE_NUMBER)
79 }
80
81 }else{
82 // be hash / properties
83 replacementValue = rest[0][varName];
84 if (replacementValue == undefined){
85 // check for bad variable names
86 var errorMsg : String = "Var name:'" + varName + "' not found on " + rest[0];
87 throw new Error(errorMsg);
88 }
89
90
91 }
92 // format the string accodingly to the formatter
93 if (formater == STRING_FORMATTER){
94 match.replacement = replacementValue.toString();
95 }else if (formater == FLOAT_FORMATER){
96 // floats, check if we need to truncate precision
97 if (precision){
98 match.replacement = truncateNumber(Number(replacementValue), int(precision)).toString()
99 }else{
100 match.replacement = replacementValue.toString();
101 }
102 }else if (formater == OCTAL_FORMATER){
103 match.replacement = int(replacementValue).toString(8);
104 }else if (formater == HEXA_FORMATER){
105 match.replacement = "0x" + int(replacementValue).toString(16);
106 }else if(DATES_FORMATERS.indexOf(formater) > -1){
107 switch (formater){
108 case DATE_DAY_FORMATTER:
109 match.replacement = replacementValue.date;
110 break
111 case DATE_FULLYEAR_FORMATTER:
112 match.replacement = replacementValue.fullYear;
113 break
114 case DATE_YEAR_FORMATTER:
115 match.replacement = replacementValue.fullYear.toString().substr(2,2);
116 break
117 case DATE_MONTH_FORMATTER:
118 match.replacement = replacementValue.month + 1;
119 break
120 case DATE_HOUR24_FORMATTER:
121 match.replacement = replacementValue.hours;
122 break
123 case DATE_HOUR_FORMATTER:
124 var hours24 : Number = replacementValue.hours;
125 match.replacement = (hours24 -12).toString();
126 break
127 case DATE_HOUR_AMPM_FORMATTER:
128 match.replacement = (replacementValue.hours >= 12 ? "p.m" : "a.m");
129 break
130 case DATE_TOLOCALE_FORMATTER:
131 match.replacement = replacementValue.toLocaleString();
132 break
133 case DATE_MINUTES_FORMATTER:
134 match.replacement = replacementValue.minutes;
135 break
136 case DATE_SECONDS_FORMATTER:
137 match.replacement = replacementValue.seconds;
138 break
139 }
140 }else{
141 trace("no good replacment " );
142 }
143 matches.push(match);
144 // just a small check in case we get stuck: kludge!
145 runs ++;
146 if (runs > 10000){
147 trace("something is wrong, breaking out")
148 break
149 }
150 numMatches ++;
151 // iterates next match
152 result = SUBS_RE.exec(raw);
153 }
154 // in case there's nothing to substitute, just return the initial string
155 if(matches.length == 0){
156 trace("no matches, returning" );
157 return raw;
158 }
159 // now actually do the substitution, keeping a buffer to be joined at
160 //the end for better performance
161 var buffer : Array = [];
162 var lastMatch : Match;
163 // beggininf os string, if it doesn't start with a substitition
164 var previous : String = raw.substr(0, matches[0].startIndex);
165 var subs : String;
166 for each(match in matches){
167 // finds out the previous string part and the next substitition
168 if (lastMatch){
169 previous = raw.substring(lastMatch.endIndex , match.startIndex);
170 }
171 buffer.push(previous);
172 buffer.push(match.replacement);
173 lastMatch = match;
174
175 }
176 // buffer the tail of the string: text after the last substitution
177 buffer.push(raw.substr(match.endIndex, raw.length - match.endIndex));
178 return buffer.join("");
179 }
180 }
181
182
183// internal usage
184/** @private */
185const BAD_VARIABLE_NUMBER : String = "The number of variables to be replaced and template holes don't match";
186/** Converts to a string*/
187const STRING_FORMATTER : String = "s";
188/** Outputs as a Number, can use the precision specifier: %.2sf will output a float with 2 decimal digits.*/
189const FLOAT_FORMATER : String = "f";
190/** Converts to an OCTAL number */
191const OCTAL_FORMATER : String = "o";
192/** Converts to a Hexa number (includes 0x) */
193const HEXA_FORMATER : String = "x";
194/** @private */
195const DATES_FORMATERS : String = "aAbBcdHIjmMpSUwWxXyYZ";
196/** Day of month, from 0 to 30 on <code>Date</code> objects.*/
197const DATE_DAY_FORMATTER : String = "d";
198/** Full year, e.g. 2007 on <code>Date</code> objects.*/
199const DATE_FULLYEAR_FORMATTER : String = "Y";
200/** Year, e.g. 07 on <code>Date</code> objects.*/
201const DATE_YEAR_FORMATTER : String = "y";
202/** Month from 1 to 12 on <code>Date</code> objects.*/
203const DATE_MONTH_FORMATTER : String = "m";
204/** Hours (0-23) on <code>Date</code> objects.*/
205const DATE_HOUR24_FORMATTER : String = "H";
206/** Hours 0-12 on <code>Date</code> objects.*/
207const DATE_HOUR_FORMATTER : String = "I";
208/** a.m or p.m on <code>Date</code> objects.*/
209const DATE_HOUR_AMPM_FORMATTER : String = "p";
210/** Minutes on <code>Date</code> objects.*/
211const DATE_MINUTES_FORMATTER : String = "M";
212/** Seconds on <code>Date</code> objects.*/
213const DATE_SECONDS_FORMATTER : String = "S";
214/** A string rep of a <code>Date</code> object on the current locale.*/
215const DATE_TOLOCALE_FORMATTER : String = "c";
216
217var version : String = "$Id: printf.as 5 2008-08-01 12:18:25Z debert $"
218
219
220
221/** @private
222 * Internal class that normalizes matching information.
223 */
224class Match{
225 public var startIndex : int;
226 public var endIndex : int;
227 public var length : int;
228 public var content : String;
229 public var replacement : String;
230 public var before : String;
231 public function toString() : String{
232 return "Match [" + startIndex + " - " + endIndex + "] (" + length + ") " + content + ", replacement:" +replacement + ";"
233 }
234}
235/** @private */
236function truncateNumber(raw : Number, decimals :int =2) : Number {
237 var power : int = Math.pow(10, decimals);
238 return Math.round(raw * ( power )) / power;
239}
Note: See TracBrowser for help on using the repository browser.