0030058: Visualization, Select3D_SensitivePrimitiveArray - the selection is not fast...
[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
96f3bacc 18#include <NCollection_Buffer.hxx>
ddf2fe8e 19#include <NCollection_DataMap.hxx>
20#include <OSD_Environment.hxx>
7fd59977 21#include <TCollection_AsciiString.hxx>
22#include <TCollection_ExtendedString.hxx>
c67cd62e 23#include <Standard_Mutex.hxx>
94708556 24#include <OSD_OpenFile.hxx>
ddf2fe8e 25
7fd59977 26#include <stdlib.h>
27#include <stdio.h>
28
ddf2fe8e 29typedef NCollection_DataMap<TCollection_AsciiString,TCollection_ExtendedString> Message_DataMapOfExtendedString;
7fd59977 30
31static Message_DataMapOfExtendedString& msgsDataMap ()
32{
33 static Message_DataMapOfExtendedString aDataMap;
34 return aDataMap;
35}
36
c67cd62e 37// mutex used to prevent concurrent access to message registry
38static Standard_Mutex theMutex;
39
7fd59977 40typedef enum
41{
42 MsgFile_WaitingKeyword,
43 MsgFile_WaitingMessage,
44 MsgFile_WaitingMoreMessage,
45 MsgFile_Indefinite
46} LoadingState;
47
48//=======================================================================
49//function : Message_MsgFile
50//purpose : Load file from given directories
51// theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
52//=======================================================================
53
54Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
55 const Standard_CString theFileName)
56{
57 if ( ! theDirName || ! theFileName ) return Standard_False;
58
59 Standard_Boolean ret = Standard_True;
60 TCollection_AsciiString aDirList (theDirName);
61 // Try to load from all consecutive directories in list
62 for (int i = 1;; i++)
63 {
64 TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
65 if (aFileName.IsEmpty()) break;
57c28b61 66#ifdef _WIN32
7fd59977 67 aFileName += '\\';
68#else
69 aFileName += '/';
70#endif
71 aFileName += theFileName;
72 if ( ! LoadFile (aFileName.ToCString()) )
73 ret = Standard_False;
74 }
75 return ret;
76}
77
78//=======================================================================
79//function : getString
80//purpose : Takes a TCollection_ExtendedString from Ascii or Unicode
81// Strings are left-trimmed; those beginning with '!' are omitted
82//Called : from loadFile()
83//=======================================================================
84
85template <class _Char> static inline Standard_Boolean
86getString (_Char *& thePtr,
87 TCollection_ExtendedString& theString,
88 Standard_Integer& theLeftSpaces)
89{
90 _Char * anEndPtr = thePtr;
91 _Char * aPtr;
92 Standard_Integer aLeftSpaces;
93
94 do
95 {
96 // Skip whitespaces in the beginning of the string
97 aPtr = anEndPtr;
98 aLeftSpaces = 0;
302f96fb 99 for (;;)
7fd59977 100 {
101 _Char aChar = * aPtr;
102 if (aChar == ' ') aLeftSpaces++;
103 else if (aChar == '\t') aLeftSpaces += 8;
104 else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
105 else break;
106 aPtr++;
107 }
108
109 // Find the end of the string
110 for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
111 if (anEndPtr[0] == '\n')
112 {
113 if (anEndPtr[-1] == '\r') anEndPtr--;
114 break;
115 }
116
117 } while (aPtr[0] == '!');
118
119 // form the result
120 if (aPtr == anEndPtr) return Standard_False;
121 thePtr = anEndPtr;
122 if (*thePtr)
123 *thePtr++ = '\0';
124 theString = TCollection_ExtendedString (aPtr);
125 theLeftSpaces = aLeftSpaces;
126 return Standard_True;
127}
128
129//=======================================================================
130//function : loadFile
131//purpose : Static function, fills the DataMap of Messages from Ascii or Unicode
132//Called : from LoadFile()
133//=======================================================================
134
135template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
136{
137 TCollection_AsciiString aKeyword;
138 TCollection_ExtendedString aMessage, aString;
139 LoadingState aState = MsgFile_WaitingKeyword;
140 _Char * sCurrentString = theBuffer;
141 Standard_Integer aLeftSpaces=0, aFirstLeftSpaces = 0;
142
143 // Take strings one-by-one; comments already screened
144 while (::getString (sCurrentString, aString, aLeftSpaces))
145 {
146 Standard_Boolean isKeyword = (aString.Value(1) == '.');
147 switch (aState)
148 {
149 case MsgFile_WaitingMoreMessage:
150 if (isKeyword)
151 Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
152 // Pass from here to 'case MsgFile_WaitingKeyword'
153 else
154 {
155 // Add another line to the message already in the buffer 'aMessage'
156 aMessage += '\n';
157 aLeftSpaces -= aFirstLeftSpaces;
158 if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
159 aMessage += aString;
160 break;
161 }
b1811c1d 162 Standard_FALLTHROUGH
7fd59977 163 case MsgFile_WaitingMessage:
164 if (isKeyword == Standard_False)
165 {
166 aMessage = aString;
167 aFirstLeftSpaces = aLeftSpaces; // remember the starting position
168 aState = MsgFile_WaitingMoreMessage;
169 break;
170 }
171 // Pass from here to 'case MsgFile_WaitingKeyword'
b1811c1d 172 Standard_FALLTHROUGH
7fd59977 173 case MsgFile_WaitingKeyword:
174 if (isKeyword)
175 {
176 // remove the first dot character and all subsequent spaces + right-trim
177 aKeyword = TCollection_AsciiString (aString.Split(1));
178 aKeyword.LeftAdjust();
179 aKeyword.RightAdjust();
180 aState = MsgFile_WaitingMessage;
181 }
182 break;
183 default:
184 break;
185 }
186 }
187 // Process the last string still remaining in the buffer
188 if (aState == MsgFile_WaitingMoreMessage)
189 Message_MsgFile::AddMsg (aKeyword, aMessage);
190 return Standard_True;
191}
192
193//=======================================================================
194//function : GetFileSize
195//purpose :
196//=======================================================================
197
198static Standard_Integer GetFileSize (FILE *theFile)
199{
200 if ( !theFile ) return -1;
201
202 // get real file size
203 long nRealFileSize = 0;
204 if ( fseek(theFile, 0, SEEK_END) != 0 ) return -1;
205 nRealFileSize = ftell(theFile);
206 if ( fseek(theFile, 0, SEEK_SET) != 0 ) return -1;
207
208 return (Standard_Integer) nRealFileSize;
209}
210
211//=======================================================================
212//function : LoadFile
213//purpose : Load the list of messages from a file
214//=======================================================================
215
216Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
217{
218 if (theFileName == NULL || * theFileName == '\0') return Standard_False;
219
220 // Open the file
94708556 221 FILE *anMsgFile = OSD_OpenFile(theFileName,"rb");
96f3bacc 222 if (!anMsgFile)
223 return Standard_False;
7fd59977 224
96f3bacc 225 const Standard_Integer aFileSize = GetFileSize (anMsgFile);
226 NCollection_Buffer aBuffer(NCollection_BaseAllocator::CommonBaseAllocator());
227 if (aFileSize <= 0 || !aBuffer.Allocate(aFileSize + 2))
7fd59977 228 {
229 fclose (anMsgFile);
230 return Standard_False;
231 }
96f3bacc 232
233 char* anMsgBuffer = reinterpret_cast<char*>(aBuffer.ChangeData());
234 const Standard_Integer nbRead =
235 static_cast<Standard_Integer>( fread(anMsgBuffer, 1, aFileSize, anMsgFile) );
236
7fd59977 237 fclose (anMsgFile);
238 if (nbRead != aFileSize)
239 return Standard_False;
96f3bacc 240
7fd59977 241 anMsgBuffer[aFileSize] = 0;
96f3bacc 242 anMsgBuffer[aFileSize + 1] = 0;
7fd59977 243
244 // Read the messages in the file and append them to the global DataMap
245 Standard_Boolean isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe');
246 Standard_Boolean isBigEndian = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff');
247 if ( isLittleEndian || isBigEndian )
248 {
96f3bacc 249 Standard_ExtCharacter* aUnicodeBuffer =
250 reinterpret_cast<Standard_ExtCharacter*>(&anMsgBuffer[2]);
7fd59977 251 // Convert Unicode representation to order adopted on current platform
252#if defined(__sparc) && defined(__sun)
253 if ( isLittleEndian )
254#else
255 if ( isBigEndian )
256#endif
257 {
258 // Reverse the bytes throughout the buffer
96f3bacc 259 const Standard_ExtCharacter* const anEnd =
260 reinterpret_cast<const Standard_ExtCharacter* const>(&anMsgBuffer[aFileSize]);
261
262 for (Standard_ExtCharacter* aPtr = aUnicodeBuffer; aPtr < anEnd; aPtr++)
7fd59977 263 {
96f3bacc 264 unsigned short aWord = *aPtr;
265 *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
7fd59977 266 }
267 }
268 return ::loadFile (aUnicodeBuffer);
269 }
270 else
96f3bacc 271 return ::loadFile (anMsgBuffer);
7fd59977 272}
273
274//=======================================================================
275//function : LoadFromEnv
ee5befae 276//purpose :
7fd59977 277//=======================================================================
ee5befae 278Standard_Boolean Message_MsgFile::LoadFromEnv (const Standard_CString theEnvName,
279 const Standard_CString theFileName,
280 const Standard_CString theLangExt)
7fd59977 281{
ee5befae 282 TCollection_AsciiString aLangExt (theLangExt != NULL ? theLangExt : "");
283 if (aLangExt.IsEmpty())
284 {
285 OSD_Environment aLangEnv ("CSF_LANGUAGE");
286 aLangExt = aLangEnv.Value();
287 if (aLangExt.IsEmpty())
288 {
289 aLangExt = "us";
290 }
7fd59977 291 }
ee5befae 292
293 TCollection_AsciiString aFilePath (theFileName);
294 if (theEnvName != NULL
295 && theEnvName[0] != '\0')
296 {
297 OSD_Environment aNameEnv (theEnvName);
298 TCollection_AsciiString aNameEnvStr = aNameEnv.Value();
299 if (!aNameEnvStr.IsEmpty())
300 {
301 if (aNameEnvStr.Value (aNameEnvStr.Length()) != '/')
302 {
303 aFilePath.Insert (1, '/');
304 }
305 aFilePath.Insert (1, aNameEnvStr);
7fd59977 306 }
307 }
7fd59977 308
ee5befae 309 if (aLangExt.Value (1) != '.')
310 {
311 aFilePath.AssignCat ('.');
312 }
313 aFilePath.AssignCat (aLangExt);
314
315 return Message_MsgFile::LoadFile (aFilePath.ToCString());
316}
317
318//=======================================================================
319//function : LoadFromString
320//purpose :
321//=======================================================================
322Standard_Boolean Message_MsgFile::LoadFromString (const Standard_CString theContent,
323 const Standard_Integer theLength)
324{
325 Standard_Integer aStringSize = theLength >= 0 ? theLength : (Standard_Integer )strlen (theContent);
326 NCollection_Buffer aBuffer (NCollection_BaseAllocator::CommonBaseAllocator());
327 if (aStringSize <= 0 || !aBuffer.Allocate (aStringSize + 2))
328 {
329 return Standard_False;
330 }
331
332 memcpy (aBuffer.ChangeData(), theContent, aStringSize);
333 aBuffer.ChangeData()[aStringSize + 0] = '\0';
334 aBuffer.ChangeData()[aStringSize + 1] = '\0';
335 char* anMsgBuffer = reinterpret_cast<char*>(aBuffer.ChangeData());
336 return ::loadFile (anMsgBuffer);
7fd59977 337}
338
339//=======================================================================
340//function : AddMsg
341//purpose : Add one message to the global table. Fails if the same keyword
342// already exists in the table
343//=======================================================================
344
345Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
346 const TCollection_ExtendedString& theMessage)
347{
348 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
c67cd62e 349
350 Standard_Mutex::Sentry aSentry (theMutex);
7fd59977 351 aDataMap.Bind (theKeyword, theMessage);
352 return Standard_True;
353}
354
355//=======================================================================
356//function : getMsg
357//purpose : retrieve the message previously defined for the given keyword
358//=======================================================================
359
360const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
361{
362 TCollection_AsciiString aKey((char*)theKeyword);
363 return Msg (aKey);
364}
365
366//=======================================================================
c67cd62e 367//function : HasMsg
368//purpose :
369//=======================================================================
370
371Standard_Boolean Message_MsgFile::HasMsg (const TCollection_AsciiString& theKeyword)
372{
373 Standard_Mutex::Sentry aSentry (theMutex);
374 return ::msgsDataMap().IsBound (theKeyword);
375}
376
377//=======================================================================
378//function : Msg
7fd59977 379//purpose : retrieve the message previously defined for the given keyword
380//=======================================================================
381
382const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
383{
384 // find message in the map
385 Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
c67cd62e 386 Standard_Mutex::Sentry aSentry (theMutex);
387
388 // if message is not found, generate error message and add it to the map to minimize overhead
389 // on consequent calls with the same key
390 if (! aDataMap.IsBound(theKeyword))
391 {
392 // text of the error message can be itself defined in the map
393 static const TCollection_AsciiString aPrefixCode("Message_Msg_BadKeyword");
394 static const TCollection_ExtendedString aDefPrefix("Unknown message invoked with the keyword ");
395 TCollection_AsciiString aErrorMessage = (aDataMap.IsBound(aPrefixCode) ? aDataMap(aPrefixCode) : aDefPrefix);
396 aErrorMessage += theKeyword;
397 aDataMap.Bind (theKeyword, aErrorMessage); // do not use AddMsg() here to avoid mutex deadlock
fa523cdd 398 }
c67cd62e 399
400 return aDataMap (theKeyword);
7fd59977 401}