Oolite
Loading...
Searching...
No Matches
OOEncodingConverter.m
Go to the documentation of this file.
1/*
2
3OOEncodingConverter.m
4
5Copyright (C) 2008-2013 Jens Ayton and contributors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#ifndef OOENCODINGCONVERTER_EXCLUDE
28
30#import "OOCache.h"
32#import "OOLogging.h"
33
34
35/* Using compatibility mapping - converting strings to Unicode form KC - would
36 reduce potential complications in localizing Oolite. However, the method to
37 perform the transformation is not available in GNUstep. I'm currently not
38 using it under OS X either, for cross-platform consistency.
39 -- Ahruman 2008-01-27
40*/
41#if OOLITE_MAC_OS_X
42#define USE_COMPATIBILITY_MAPPING 0
43#else
44#define USE_COMPATIBILITY_MAPPING 0
45#endif
46
47
48#define PROFILE_ENCODING_CONVERTER 0
49
50
51static const NSUInteger kCachePruneThreshold = 200;
52
53
54#if PROFILE_ENCODING_CONVERTER
55static OOEncodingConverter *sProfiledConverter = nil;
56static NSTimer *sProfileTimer = nil;
57
58static unsigned sCacheHits = 0;
59static unsigned sCacheMisses = 0;
60#endif
61
62
63@interface OOEncodingConverter (Private)
64
65- (NSData *) performConversionForString:(NSString *)string;
66
67@end
68
69
70@implementation OOEncodingConverter
71
72- (id) initWithEncoding:(NSStringEncoding)encoding substitutions:(NSDictionary *)substitutions
73{
74 self = [super init];
75 if (self != nil)
76 {
77 _cache = [[OOCache alloc] init];
78 [_cache setPruneThreshold:kCachePruneThreshold];
79 [_cache setName:@"Text encoding"];
80 _substitutions = [substitutions copy];
81 _encoding = encoding;
82
83#if PROFILE_ENCODING_CONVERTER
84 if (sProfiledConverter == nil)
85 {
86 sProfiledConverter = self;
87 sProfileTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(profileFire:) userInfo:nil repeats:YES];
88 }
89#endif
90 }
91
92 return self;
93}
94
95
96- (id) initWithFontPList:(NSDictionary *)fontPList
97{
98 return [self initWithEncoding:EncodingFromString([fontPList oo_stringForKey:@"encoding"]) substitutions:[fontPList oo_dictionaryForKey:@"substitutions"]];
99}
100
101
102- (void) dealloc
103{
104 [_cache release];
105 [_substitutions release];
106
107#if PROFILE_ENCODING_CONVERTER
108 sProfiledConverter = nil;
109 [sProfileTimer invalidate];
110 sProfileTimer = nil;
111 sCacheHits = 0;
112 sCacheMisses = 0;
113#endif
114
115 [super dealloc];
116}
117
118
119- (NSString *) descriptionComponents
120{
121 return [NSString stringWithFormat:@"encoding: %u", _encoding];
122}
123
124
125- (NSData *) convertString:(NSString *)string
126{
127 NSData *data = nil;
128
129#if USE_COMPATIBILITY_MAPPING
130 // Convert to Unicode Normalization Form KC (that is, minimize the use of combining modifiers while avoiding precomposed ligatures)
131 string = [string precomposedStringWithCompatibilityMapping];
132#endif
133
134 if (string == nil) return [NSData data];
135
136 data = [_cache objectForKey:string];
137 if (data == nil)
138 {
139 data = [self performConversionForString:string];
140 if (data != nil) [_cache setObject:data forKey:string];
141
142#if PROFILE_ENCODING_CONVERTER
143 ++sCacheMisses;
144 }
145 else
146 {
147 ++sCacheHits;
148#endif
149 }
150
151 return data;
152}
153
154
155- (NSStringEncoding) encoding
156{
157 return _encoding;
158}
159
160@end
161
162
163@implementation OOEncodingConverter (Private)
164
165- (NSData *) performConversionForString:(NSString *)string
166{
167 NSString *subst = nil;
168 NSMutableString *mutable = nil;
169
170 mutable = [[string mutableCopy] autorelease];
171 if (mutable == nil) return nil;
172
173 foreachkey (subst, _substitutions)
174 {
175 [mutable replaceOccurrencesOfString:subst
176 withString:[_substitutions objectForKey:subst]
177 options:0
178 range:NSMakeRange(0, [mutable length])];
179 }
180
181 return [mutable dataUsingEncoding:_encoding allowLossyConversion:YES];
182}
183
184
185#if PROFILE_ENCODING_CONVERTER
186/*
187 Profiling observations:
188 * The clock generates one new string per second.
189 * The trade screens each use over 100 strings, so cache sizes below 150
190 are undesireable.
191 * Cache hit ratio is extremely near 100% at most times.
192*/
193- (void) profileFire:(id)junk
194{
195 float ratio = (float)sCacheHits / (float)(sCacheHits + sCacheMisses);
196 OOLog(@"strings.encoding.profile", @"Cache hits: %u, misses: %u, ratio: %.2g", sCacheHits, sCacheMisses, ratio);
197 sCacheHits = sCacheMisses = 0;
198}
199#endif
200
201@end
202
203#endif //OOENCODINGCONVERTER_EXCLUDE
204
205
206/*
207 There are a variety of overlapping naming schemes for text encoding.
208 We ignore them and use a fixed list:
209 "windows-latin-1" NSWindowsCP1252StringEncoding
210 "windows-latin-2" NSWindowsCP1250StringEncoding
211 "windows-cyrillic" NSWindowsCP1251StringEncoding
212 "windows-greek" NSWindowsCP1253StringEncoding
213 "windows-turkish" NSWindowsCP1254StringEncoding
214*/
215
216#define kWindowsLatin1Str @"windows-latin-1"
217#define kWindowsLatin2Str @"windows-latin-2"
218#define kWindowsCyrillicStr @"windows-cyrillic"
219#define kWindowsGreekStr @"windows-greek"
220#define kWindowsTurkishStr @"windows-turkish"
221
222
223NSString *StringFromEncoding(NSStringEncoding encoding)
224{
225 switch (encoding)
226 {
227 case NSWindowsCP1252StringEncoding:
228 return kWindowsLatin1Str;
229
230 case NSWindowsCP1250StringEncoding:
231 return kWindowsLatin2Str;
232
233 case NSWindowsCP1251StringEncoding:
234 return kWindowsCyrillicStr;
235
236 case NSWindowsCP1253StringEncoding:
237 return kWindowsGreekStr;
238
239 case NSWindowsCP1254StringEncoding:
240 return kWindowsTurkishStr;
241
242 default:
243 return nil;
244 }
245}
246
247
248NSStringEncoding EncodingFromString(NSString *name)
249{
250 if ([name isEqualToString:kWindowsLatin1Str]) return NSWindowsCP1252StringEncoding;
251 if ([name isEqualToString:kWindowsLatin2Str]) return NSWindowsCP1250StringEncoding;
252 if ([name isEqualToString:kWindowsCyrillicStr]) return NSWindowsCP1251StringEncoding;
253 if ([name isEqualToString:kWindowsGreekStr]) return NSWindowsCP1253StringEncoding;
254 if ([name isEqualToString:kWindowsTurkishStr]) return NSWindowsCP1254StringEncoding;
255 return (NSStringEncoding)NSNotFound;
256}
#define foreachkey(VAR, DICT)
Definition OOCocoa.h:353
#define kWindowsCyrillicStr
static const NSUInteger kCachePruneThreshold
#define kWindowsLatin1Str
NSStringEncoding EncodingFromString(NSString *name)
#define kWindowsLatin2Str
#define kWindowsGreekStr
NSString * StringFromEncoding(NSStringEncoding encoding)
#define kWindowsTurkishStr
#define OOLog(class, format,...)
Definition OOLogging.h:88
return nil