0024911: Avoid using virtual functions in NCollection classes
[occt.git] / src / Message / Message_MsgFile.cxx
CommitLineData
b311480e 1// Created on: 2001-04-26
2// Created by: OCC Team
973c2be1 3// Copyright (c) 2001-2014 OPEN CASCADE SAS
b311480e 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
b311480e 6//
d5f74e42 7// This library is free software; you can redistribute it and/or modify it under
8// the terms of the GNU Lesser General Public License version 2.1 as published
973c2be1 9// by the Free Software Foundation, with special exception defined in the file
10// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11// distribution for complete text of the license and disclaimer of any warranty.
b311480e 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
7fd59977 15
16#include <Message_MsgFile.hxx>
17
ddf2fe8e 18#include <NCollection_DataMap.hxx>
19#include <OSD_Environment.hxx>
7fd59977 20#include <TCollection_AsciiString.hxx>
21#include <TCollection_ExtendedString.hxx>
ddf2fe8e 22
7fd59977 23#include <stdlib.h>
24#include <stdio.h>
25
ddf2fe8e 26typedef NCollection_DataMap<TCollection_AsciiString,TCollection_ExtendedString> Message_DataMapOfExtendedString;
7fd59977 27
28static Message_DataMapOfExtendedString& msgsDataMap ()
29{
30 static Message_DataMapOfExtendedString aDataMap;
31 return aDataMap;
32}
33
34typedef enum
35{
36 MsgFile_WaitingKeyword,
37 MsgFile_WaitingMessage,
38 MsgFile_WaitingMoreMessage,
39 MsgFile_Indefinite
40} LoadingState;
41
42//=======================================================================
43//function : Message_MsgFile
44//purpose : Load file from given directories
45// theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
46//=======================================================================
47
48Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
49 const Standard_CString theFileName)
50{
51 if ( ! theDirName || ! theFileName ) return Standard_False;
52
53 Standard_Boolean ret = Standard_True;
54 TCollection_AsciiString aDirList (theDirName);
55 // Try to load from all consecutive directories in list
56 for (int i = 1;; i++)
57 {
58 TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
59 if (aFileName.IsEmpty()) break;
60#ifdef WNT
61 aFileName += '\\';
62#else
63 aFileName += '/';
64#endif
65 aFileName += theFileName;
66 if ( ! LoadFile (aFileName.ToCString()) )
67 ret = Standard_False;
68 }
69 return ret;
70}
71
72//=======================================================================
73//function : getString
74//purpose : Takes a TCollection_ExtendedString from Ascii or Unicode
75// Strings are left-trimmed; those beginning with '!' are omitted
76//Called : from loadFile()
77//=======================================================================
78
79template <class _Char> static inline Standard_Boolean
80getString (_Char *& thePtr,
81 TCollection_ExtendedString& theString,
82 Standard_Integer& theLeftSpaces)
83{
84 _Char * anEndPtr = thePtr;
85 _Char * aPtr;
86 Standard_Integer aLeftSpaces;
87
88 do
89 {
90 // Skip whitespaces in the beginning of the string
91 aPtr = anEndPtr;
92 aLeftSpaces = 0;
302f96fb 93 for (;;)
7fd59977 94 {
95 _Char aChar = * aPtr;
96 if (aChar == ' ') aLeftSpaces++;
97 else if (aChar == '\t') aLeftSpaces += 8;
98 else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
99 else break;
100 aPtr++;
101 }
102
103 // Find the end of the string
104 for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
105 if (anEndPtr[0] == '\n')
106 {
107 if (anEndPtr[-1] == '\r') anEndPtr--;
108 break;
109 }
110
111 } while (aPtr[0] == '!');
112
113 // form the result
114 if (aPtr == anEndPtr) return Standard_False;
115 thePtr = anEndPtr;
116 if (*thePtr)
117 *thePtr++ = '\0';
118 theString = TCollection_ExtendedString (aPtr);
119 theLeftSpaces = aLeftSpaces;
120 return Standard_True;
121}
122
123//=======================================================================
124//function : loadFile
125//purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
126//Called : from LoadFile()
127//=======================================================================
128
129template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
130{
131 TCollection_AsciiString aKeyword;
132 TCollection_ExtendedString aMessage, aString;
133 LoadingState aState = MsgFile_WaitingKeyword;
134 _Char * sCurrentString = theBuffer;
135 Standard_Integer aLeftSpaces=0, aFirstLeftSpaces = 0;
136
137 // Take strings one-by-one; comments already screened
138 while (::getString (sCurrentString, aString, aLeftSpaces))
139 {
140 Standard_Boolean isKeyword = (aString.Value(1) == '.');
141 switch (aState)
142 {
143 case MsgFile_WaitingMoreMessage:
144 if (isKeyword)
145 Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
146 // Pass from here to 'case MsgFile_WaitingKeyword'
147 else
148 {
149 // Add another line to the message already in the buffer 'aMessage'
150 aMessage += '\n';
151 aLeftSpaces -= aFirstLeftSpaces;
152 if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
153 aMessage += aString;
154 break;
155 }
156 case MsgFile_WaitingMessage:
157 if (isKeyword == Standard_False)
158 {
159 aMessage = aString;
160 aFirstLeftSpaces = aLeftSpaces; // remember the starting position
161 aState = MsgFile_WaitingMoreMessage;
162 break;
163 }
164 // Pass from here to 'case MsgFile_WaitingKeyword'
165 case MsgFile_WaitingKeyword:
166 if (isKeyword)
167 {
168 // remove the first dot character and all subsequent spaces + right-trim
169 aKeyword = TCollection_AsciiString (aString.Split(1));
170 aKeyword.LeftAdjust();
171 aKeyword.RightAdjust();
172 aState = MsgFile_WaitingMessage;
173 }
174 break;
175 default:
176 break;
177 }
178 }
179 // Process the last string still remaining in the buffer
180 if (aState == MsgFile_WaitingMoreMessage)
181 Message_MsgFile::AddMsg (aKeyword, aMessage);
182 return Standard_True;
183}
184
185//=======================================================================
186//function : GetFileSize
187//purpose :
188//=======================================================================
189
190static Standard_Integer GetFileSize (FILE *theFile)
191{
192 if ( !theFile ) return -1;
193
194 // get real file size
195 long nRealFileSize = 0;
196 if ( fseek(theFile, 0, SEEK_END) != 0 ) return -1;
197 nRealFileSize = ftell(theFile);
198 if ( fseek(theFile, 0, SEEK_SET) != 0 ) return -1;
199
200 return (Standard_Integer) nRealFileSize;
201}
202
203//=======================================================================
204//function : LoadFile
205//purpose : Load the list of messages from a file
206//=======================================================================
207
208Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
209{
210 if (theFileName == NULL || * theFileName == '\0') return Standard_False;
211
212 // Open the file
213 FILE *anMsgFile = fopen (theFileName, "rb");
214 if (!anMsgFile) return Standard_False;
215
216 // Read the file into memory
217 class Buffer
218 {
219 // self-destructing buffer
220 char *myBuf;
221 public:
222 Buffer (Standard_Integer theSize) : myBuf(new char [theSize]) {}
223 ~Buffer () { delete [] myBuf; }
224 operator char* () const { return myBuf; }
225 char& operator [] (Standard_Integer theInd) { return myBuf[theInd]; }
226 };
227 Standard_Integer aFileSize = GetFileSize (anMsgFile);
228 if (aFileSize <= 0)
229 {
230 fclose (anMsgFile);
231 return Standard_False;
232 }
233 Buffer anMsgBuffer (aFileSize + 2);
234 Standard_Integer nbRead =
235 (Standard_Integer) fread (anMsgBuffer, 1, aFileSize, anMsgFile);
236 fclose (anMsgFile);
237 if (nbRead != aFileSize)
238 return Standard_False;
239 anMsgBuffer[aFileSize] = 0;
240 anMsgBuffer[aFileSize+1] = 0;
241
242 // Read the messages in the file and append them to the global DataMap
243 Standard_Boolean isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe');
244 Standard_Boolean isBigEndian = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff');
245 if ( isLittleEndian || isBigEndian )
246 {
247 Standard_ExtCharacter * aUnicodeBuffer =
248 (Standard_ExtCharacter *) &anMsgBuffer[2];
249 // Convert Unicode representation to order adopted on current platform
250#if defined(__sparc) && defined(__sun)
251 if ( isLittleEndian )
252#else
253 if ( isBigEndian )
254#endif
255 {
256 // Reverse the bytes throughout the buffer
257 for (Standard_ExtCharacter * aPtr = aUnicodeBuffer;
258 aPtr < (Standard_ExtCharacter *) &anMsgBuffer[aFileSize]; aPtr++)
259 {
260 unsigned short aWord = *aPtr;
261 *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
262 }
263 }
264 return ::loadFile (aUnicodeBuffer);
265 }
266 else
267 return ::loadFile ((char*) anMsgBuffer);
268}
269
270//=======================================================================
271//function : LoadFromEnv
272//purpose :
273//=======================================================================
274void Message_MsgFile::LoadFromEnv
275 (const Standard_CString envname,
276 const Standard_CString filename,
277 const Standard_CString ext)
278{
279 Standard_CString extname = ext;
280 TCollection_AsciiString extstr;
281 if (!extname || extname[0] == '\0') {
282 OSD_Environment extenv("CSF_LANGUAGE");
283 extstr = extenv.Value();
284 extname = extstr.ToCString();
285 }
286 if (!extname || extname[0] == '\0') extname = "us";
287
288 TCollection_AsciiString filestr(filename);
289 if (envname && envname[0] != '\0') {
290 OSD_Environment envenv(envname);
291 TCollection_AsciiString envstr = envenv.Value();
292 if (envstr.Length() > 0) {
293 if (envstr.Value(envstr.Length()) != '/') filestr.Insert (1,'/');
294 filestr.Insert (1,envstr.ToCString());
295 }
296 }
297 if (extname[0] != '.') filestr.AssignCat ('.');
298 filestr.AssignCat (extname);
299
300 Message_MsgFile::LoadFile (filestr.ToCString());
301}
302
303//=======================================================================
304//function : AddMsg
305//purpose : Add one message to the global table. Fails if the same keyword
306// already exists in the table
307//=======================================================================
308
309Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
310 const TCollection_ExtendedString& theMessage)
311{
312 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
313// if (aDataMap.IsBound (theKeyword))
314// return Standard_False;
315 aDataMap.Bind (theKeyword, theMessage);
316 return Standard_True;
317}
318
319//=======================================================================
320//function : getMsg
321//purpose : retrieve the message previously defined for the given keyword
322//=======================================================================
323
324const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
325{
326 TCollection_AsciiString aKey((char*)theKeyword);
327 return Msg (aKey);
328}
329
330//=======================================================================
331//function : getMsg
332//purpose : retrieve the message previously defined for the given keyword
333//=======================================================================
334
335const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
336{
337 // find message in the map
338 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
339 if (aDataMap.IsBound (theKeyword))
340 return aDataMap.Find (theKeyword);
341
342 // if not found, generate error message
fa523cdd
RL
343 // to minimize risk of data races when running concurrently, set the static variables
344 // only if they are empty; this gives a possibility to enforce calling this method
345 // upfront to initialize these variables and only read access them afterwards. However
346 // theKeyword is no longer appended. aDefPrefix remained unchanged to not break some
347 // logs which might expect the previous value
348 static const TCollection_ExtendedString aDefPrefix ("Unknown message invoked with the keyword");
7fd59977 349 static const TCollection_AsciiString aPrefixCode ("Message_Msg_BadKeyword");
350 static TCollection_ExtendedString aFailureMessage;
fa523cdd
RL
351 if (aFailureMessage.Length() == 0) {
352 if (aDataMap.IsBound (aPrefixCode))
353 aFailureMessage = aDataMap.Find (aPrefixCode);
354 else
355 aFailureMessage = aDefPrefix;
356 }
7fd59977 357 return aFailureMessage;
358}