TextModel interface
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / logical-model.cpp
index 01b03fd..20cd630 100644 (file)
 // CLASS HEADER
 #include <dali-toolkit/internal/text/logical-model.h>
 
+// EXTERNAL INCLUDES
+#include <memory.h>
+
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
 #include <dali-toolkit/internal/text/bidirectional-paragraph-info-run.h>
 #include <dali-toolkit/internal/text/font-run.h>
 #include <dali-toolkit/internal/text/script-run.h>
 
-// EXTERNAL INCLUDES
-#include <memory.h>
-
 namespace Dali
 {
 
@@ -35,14 +35,35 @@ namespace Toolkit
 
 namespace Text
 {
+/**
+ * @brief caches some temporary values of the GetNumberOfScriptRuns( characterIndex, numberOfCharacters )
+ * operation and the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) as they are going to be
+ * used in the GetScriptRuns() and the GetFontRuns() calls.
+ */
+struct GetRunCache
+{
+  CharacterIndex characterIndex;     ///< The character index.
+  Length         numberOfCharacters; ///< The number of characters.
+  Length         firstRun;           ///< Index to the first run.
+  Length         numberOfRuns;       ///< The number of runs.
+};
 
 struct LogicalModel::Impl
 {
-  Vector<Character>     mText;
-  Vector<ScriptRun>     mScriptRuns;
-  Vector<FontRun>       mFontRuns;
-  Vector<LineBreakInfo> mLineBreakInfo;
-  Vector<WordBreakInfo> mWordBreakInfo;
+  Vector<Character>                     mText;
+  Vector<ScriptRun>                     mScriptRuns;
+  Vector<FontRun>                       mFontRuns;
+  Vector<LineBreakInfo>                 mLineBreakInfo;
+  Vector<WordBreakInfo>                 mWordBreakInfo;
+  Vector<BidirectionalParagraphInfoRun> mBidirectionalParagraphInfo;
+
+  Vector<BidirectionalLineInfoRun>      mBidirectionalLineInfo;
+  Vector<CharacterIndex>                mLogicalToVisualMap; ///< Bidirectional logical to visual conversion table.
+  Vector<CharacterIndex>                mVisualToLogicalMap; ///< Bidirectional visual to logical conversion table.
+
+  GetRunCache                           mGetScriptCache;             ///< Caches the GetNumberOfScriptRuns( characterIndex, numberOfCharacters ) operation.
+  GetRunCache                           mGetFontCache;               ///< Caches the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) operation.
+  GetRunCache                           mGetBidirectionalCache;      ///< Caches the GetNumberOfBidirectionalInfoRuns( characterIndex, numberOfCharacters ) operation.
 };
 
 LogicalModelPtr LogicalModel::New()
@@ -71,8 +92,8 @@ Length LogicalModel::GetNumberOfCharacters() const
   return mImpl->mText.Count();
 }
 
-void LogicalModel::GetText( CharacterIndex characterIndex,
-                            Character* text,
+void LogicalModel::GetText( Character* text,
+                            CharacterIndex characterIndex,
                             Length numberOfCharacters ) const
 {
   Vector<Character>& modelText = mImpl->mText;
@@ -84,6 +105,13 @@ Character LogicalModel::GetCharacter( CharacterIndex characterIndex ) const
   return mImpl->mText[characterIndex];
 }
 
+void LogicalModel::ReplaceText( CharacterIndex characterIndex,
+                                Length numberOfCharactersToRemove,
+                                const Character* const text,
+                                Length numberOfCharactersToInsert )
+{
+}
+
 void LogicalModel::SetScripts( const ScriptRun* const scripts,
                                Length numberOfRuns )
 {
@@ -98,65 +126,82 @@ void LogicalModel::SetScripts( const ScriptRun* const scripts,
     scriptRuns.Resize( numberOfRuns );
     memcpy( scriptRuns.Begin(), scripts, numberOfRuns * sizeof( ScriptRun ) );
   }
+
+  mImpl->mGetScriptCache.characterIndex = 0u;
+  mImpl->mGetScriptCache.numberOfCharacters = 0u;
+  mImpl->mGetScriptCache.firstRun = 0u;
+  mImpl->mGetScriptCache.numberOfRuns = 0u;
 }
 
 Length LogicalModel::GetNumberOfScriptRuns( CharacterIndex characterIndex,
                                             Length numberOfCharacters ) const
 {
-  if( ( 0u == characterIndex ) && ( mImpl->mText.Count() == numberOfCharacters ) )
+  GetRunCache& scriptCache = mImpl->mGetScriptCache;
+
+  // Set the character index and the number of characters into the cache.
+  scriptCache.characterIndex = characterIndex;
+  scriptCache.numberOfCharacters = numberOfCharacters;
+
+  if( ( 0u == characterIndex ) &&
+      ( mImpl->mText.Count() == numberOfCharacters ) )
   {
-    return mImpl->mScriptRuns.Count();
+    scriptCache.firstRun = 0u;
+    scriptCache.numberOfRuns = mImpl->mScriptRuns.Count();
+    return scriptCache.numberOfRuns;
   }
 
-  const CharacterIndex charcterEndIndex = characterIndex + numberOfCharacters;
-  Length numberOfScriptRuns = 0u;
-  bool firstIndexFound = false;
+  // Initialize the number of scripts and the index to the first script.
+  scriptCache.firstRun = 0u;
+  scriptCache.numberOfRuns = 0;
+  bool firstScriptFound = false;
 
-  for( Length index = 0u, length = mImpl->mScriptRuns.Count(); index < length; ++index )
+  const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
+  const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
+
+  // Traverse the scripts and count those scripts within the range of characters.
+  for( Vector<ScriptRun>::ConstIterator it = modelScripts.Begin(),
+         endIt = modelScripts.End();
+       it != endIt;
+       ++it )
   {
-    const ScriptRun* const scriptRun = mImpl->mScriptRuns.Begin() + index;
+    const ScriptRun& script = *it;
 
-    if( !firstIndexFound &&
-        ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
+    if( ( script.characterRun.characterIndex + script.characterRun.numberOfCharacters > characterIndex ) &&
+        ( lastCharacterIndex > script.characterRun.characterIndex ) )
     {
-      // The character index is within this script run.
-      // Starts the counter of script runs.
-      firstIndexFound = true;
+      firstScriptFound = true;
+      ++scriptCache.numberOfRuns;
+    }
+    else if( lastCharacterIndex <= script.characterRun.characterIndex )
+    {
+      // nothing else to do.
+      break;
     }
 
-    if( firstIndexFound )
+    if( !firstScriptFound )
     {
-      ++numberOfScriptRuns;
-      if( scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters > charcterEndIndex )
-      {
-        // This script run exceeds the given range. The number of scripts can be returned.
-        return numberOfScriptRuns;
-      }
+      ++scriptCache.firstRun;
     }
   }
 
-  return numberOfScriptRuns;
+  return scriptCache.numberOfRuns;
 }
 
 void LogicalModel::GetScriptRuns( ScriptRun* scriptRuns,
                                   CharacterIndex characterIndex,
                                   Length numberOfCharacters ) const
 {
-  // A better implementation can cache the first script run and the number of then when the GetNumberOfScriptRuns() is called.
+  const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
+  GetRunCache& scriptCache = mImpl->mGetScriptCache;
 
-  Length numberOfScriptRuns = GetNumberOfScriptRuns( characterIndex,
-                                                     numberOfCharacters );
-
-  for( Length index = 0u, length = mImpl->mScriptRuns.Count(); index < length; ++index )
+  if( ( characterIndex != scriptCache.characterIndex ) ||
+      ( numberOfCharacters != scriptCache.numberOfCharacters ) )
   {
-    const ScriptRun* const scriptRun = mImpl->mScriptRuns.Begin() + index;
-
-    if( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters )
-    {
-      memcpy( scriptRuns, scriptRun, sizeof( ScriptRun ) * numberOfScriptRuns );
-      return;
-    }
+    GetNumberOfScriptRuns( characterIndex,
+                           numberOfCharacters );
   }
+
+  memcpy( scriptRuns, modelScripts.Begin() + scriptCache.firstRun, scriptCache.numberOfRuns * sizeof( ScriptRun ) );
 }
 
 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
@@ -177,6 +222,13 @@ Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
   return TextAbstraction::UNKNOWN;
 }
 
+void LogicalModel::ReplaceScripts( CharacterIndex characterIndex,
+                                   Length numberOfCharactersToRemove,
+                                   const ScriptRun* const scriptRuns,
+                                   Length numberOfCharactersToInsert )
+{
+}
+
 void LogicalModel::SetFonts( const FontRun* const fonts,
                              Length numberOfRuns )
 {
@@ -191,65 +243,82 @@ void LogicalModel::SetFonts( const FontRun* const fonts,
     fontRuns.Resize( numberOfRuns );
     memcpy( fontRuns.Begin(), fonts, numberOfRuns * sizeof( FontRun ) );
   }
+
+  mImpl->mGetFontCache.characterIndex = 0u;
+  mImpl->mGetFontCache.numberOfCharacters = 0u;
+  mImpl->mGetFontCache.firstRun = 0u;
+  mImpl->mGetFontCache.numberOfRuns = 0u;
 }
 
 Length LogicalModel::GetNumberOfFontRuns( CharacterIndex characterIndex,
                                           Length numberOfCharacters ) const
 {
-  if( ( 0u == characterIndex ) && ( mImpl->mText.Count() == numberOfCharacters ) )
+  GetRunCache& fontCache = mImpl->mGetFontCache;
+
+  // Set the character index and the number of characters into the cache.
+  fontCache.characterIndex = characterIndex;
+  fontCache.numberOfCharacters = numberOfCharacters;
+
+  if( ( 0u == characterIndex ) &&
+      ( mImpl->mText.Count() == numberOfCharacters ) )
   {
-    return mImpl->mFontRuns.Count();
+    fontCache.firstRun = 0u;
+    fontCache.numberOfRuns = mImpl->mFontRuns.Count();
+    return fontCache.numberOfRuns;
   }
 
-  const CharacterIndex charcterEndIndex = characterIndex + numberOfCharacters;
-  Length numberOfFontRuns = 0u;
-  bool firstIndexFound = false;
+  // Initialize the number of fonts and the index to the first font.
+  fontCache.firstRun = 0u;
+  fontCache.numberOfRuns = 0;
+  bool firstFontFound = false;
 
-  for( Length index = 0u, length = mImpl->mFontRuns.Count(); index < length; ++index )
+  const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
+  const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
+
+  // Traverse the fonts and count those fonts within the range of characters.
+  for( Vector<FontRun>::ConstIterator it = modelFonts.Begin(),
+         endIt = modelFonts.End();
+       it != endIt;
+       ++it )
   {
-    const FontRun* const fontRun = mImpl->mFontRuns.Begin() + index;
+    const FontRun& font = *it;
 
-    if( !firstIndexFound &&
-        ( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters ) )
+    if( ( font.characterRun.characterIndex + font.characterRun.numberOfCharacters > characterIndex ) &&
+        ( characterIndex + numberOfCharacters > font.characterRun.characterIndex ) )
     {
-      // The character index is within this font run.
-      // Starts the counter of font runs.
-      firstIndexFound = true;
+      firstFontFound = true;
+      ++fontCache.numberOfRuns;
+    }
+    else if( lastCharacterIndex <= font.characterRun.characterIndex )
+    {
+      // nothing else to do.
+      break;
     }
 
-    if( firstIndexFound )
+    if( !firstFontFound )
     {
-      ++numberOfFontRuns;
-      if( fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters > charcterEndIndex )
-      {
-        // This font run exceeds the given range. The number of fonts can be returned.
-        return numberOfFontRuns;
-      }
+      ++fontCache.firstRun;
     }
   }
 
-  return numberOfFontRuns;
+  return fontCache.numberOfRuns;
 }
 
 void LogicalModel::GetFontRuns( FontRun* fontRuns,
                                 CharacterIndex characterIndex,
                                 Length numberOfCharacters ) const
 {
-  // A better implementation can cache the first font run and the number of then when the GetNumberOfFontRuns() is called.
+  const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
+  GetRunCache& fontCache = mImpl->mGetFontCache;
 
-  Length numberOfFontRuns = GetNumberOfFontRuns( characterIndex,
-                                                 numberOfCharacters );
-
-  for( Length index = 0u, length = mImpl->mFontRuns.Count(); index < length; ++index )
+  if( ( characterIndex != fontCache.characterIndex ) ||
+      ( numberOfCharacters != fontCache.numberOfCharacters ) )
   {
-    const FontRun* const fontRun = mImpl->mFontRuns.Begin() + index;
-
-    if( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters )
-    {
-      memcpy( fontRuns, fontRun, sizeof( FontRun ) * numberOfFontRuns );
-      return;
-    }
+    GetNumberOfFontRuns( characterIndex,
+                         numberOfCharacters );
   }
+
+  memcpy( fontRuns, modelFonts.Begin() + fontCache.firstRun, fontCache.numberOfRuns * sizeof( FontRun ) );
 }
 
 FontId LogicalModel::GetFont( CharacterIndex characterIndex ) const
@@ -268,6 +337,13 @@ FontId LogicalModel::GetFont( CharacterIndex characterIndex ) const
   return 0u;
 }
 
+void LogicalModel::ReplaceFonts( CharacterIndex characterIndex,
+                                 Length numberOfCharactersToRemove,
+                                 const FontRun* const fontRuns,
+                                 Length numberOfCharactersToInsert )
+{
+}
+
 void LogicalModel::SetLineBreakInfo( const LineBreakInfo* const lineBreakInfo,
                                      Length length )
 {
@@ -296,6 +372,13 @@ LineBreakInfo LogicalModel::GetLineBreakInfo( CharacterIndex characterIndex ) co
   return *( mImpl->mLineBreakInfo.Begin() + characterIndex );
 }
 
+void LogicalModel::ReplaceLineBreakInfo( CharacterIndex characterIndex,
+                                         Length numberOfItemsToRemove,
+                                         const LineBreakInfo* const lineBreakInfo,
+                                         Length numberOfItemsToInsert )
+{
+}
+
 void LogicalModel::SetWordBreakInfo( const WordBreakInfo* const wordBreakInfo,
                                      Length length )
 {
@@ -324,15 +407,104 @@ WordBreakInfo LogicalModel::GetWordBreakInfo( CharacterIndex characterIndex ) co
   return *( mImpl->mWordBreakInfo.Begin() + characterIndex );
 }
 
+void LogicalModel::ReplaceWordBreakInfo( CharacterIndex characterIndex,
+                                         Length numberOfItemsToRemove,
+                                         const WordBreakInfo* const wordBreakInfo,
+                                         Length numberOfItemsToInsert )
+{
+}
+
 void LogicalModel::SetBidirectionalInfo( const BidirectionalParagraphInfoRun* const bidirectionalInfo,
                                          Length numberOfRuns )
 {
+  Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
+
+  if( 0u == numberOfRuns )
+  {
+    modelBidirectionalParagraphInfo.Clear();
+  }
+  else
+  {
+    modelBidirectionalParagraphInfo.Resize( numberOfRuns );
+    memcpy( modelBidirectionalParagraphInfo.Begin(), bidirectionalInfo, numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
+  }
+
+  mImpl->mGetBidirectionalCache.characterIndex = 0u;
+  mImpl->mGetBidirectionalCache.numberOfCharacters = 0u;
+  mImpl->mGetBidirectionalCache.firstRun = 0u;
+  mImpl->mGetBidirectionalCache.numberOfRuns = 0u;
 }
 
 Length LogicalModel::GetNumberOfBidirectionalInfoRuns( CharacterIndex characterIndex,
                                                        Length numberOfCharacters ) const
 {
-  return 0u;
+  GetRunCache& bidiCache = mImpl->mGetBidirectionalCache;
+
+  // Set the character index and the number of characters into the cache.
+  bidiCache.characterIndex = characterIndex;
+  bidiCache.numberOfCharacters = numberOfCharacters;
+
+  if( ( 0u == characterIndex ) &&
+      ( mImpl->mText.Count() == numberOfCharacters ) )
+  {
+    bidiCache.firstRun = 0u;
+    bidiCache.numberOfRuns = mImpl->mBidirectionalParagraphInfo.Count();
+    return bidiCache.numberOfRuns;
+  }
+
+  // Initialize the number of bidi paragraphs and the index to the first paragraph.
+  bidiCache.firstRun = 0u;
+  bidiCache.numberOfRuns = 0;
+  bool firstParagraphFound = false;
+
+  const Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
+
+  // Traverse the bidirectional paragraph info and count those bidi paragraphs within the range of characters.
+  for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = modelBidirectionalParagraphInfo.Begin(),
+         endIt = modelBidirectionalParagraphInfo.End();
+       it != endIt;
+       ++it )
+  {
+    const BidirectionalParagraphInfoRun& bidi = *it;
+
+    if( ( bidi.characterRun.characterIndex + bidi.characterRun.numberOfCharacters > characterIndex ) &&
+        ( characterIndex + numberOfCharacters > bidi.characterRun.characterIndex ) )
+    {
+      firstParagraphFound = true;
+      ++bidiCache.numberOfRuns;
+    }
+
+    if( !firstParagraphFound )
+    {
+      ++bidiCache.firstRun;
+    }
+  }
+
+  return bidiCache.numberOfRuns;
+}
+
+void LogicalModel::GetBidirectionalInfo( BidirectionalParagraphInfoRun* bidirectionalInfo,
+                                         CharacterIndex characterIndex,
+                                         Length numberOfCharacters ) const
+{
+  const Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
+  GetRunCache& bidiCache = mImpl->mGetBidirectionalCache;
+
+  if( ( characterIndex != bidiCache.characterIndex ) ||
+      ( numberOfCharacters != bidiCache.numberOfCharacters ) )
+  {
+    GetNumberOfBidirectionalInfoRuns( characterIndex,
+                                      numberOfCharacters );
+  }
+
+  memcpy( bidirectionalInfo, modelBidirectionalParagraphInfo.Begin() + bidiCache.firstRun, bidiCache.numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
+}
+
+void ReplaceBidirectionalInfo( CharacterIndex characterIndex,
+                               Length numberOfCharactersToRemove,
+                               const BidirectionalParagraphInfoRun* const bidirectionalInfo,
+                               Length numberOfCharactersToInsert )
+{
 }
 
 void LogicalModel::GetCharacterDirections( CharacterDirection* directions,
@@ -349,28 +521,103 @@ CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex character
 void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo,
                                           Length numberOfRuns )
 {
+  Vector<CharacterIndex>& modelVisualToLogicalMap = mImpl->mVisualToLogicalMap;
+  Vector<CharacterIndex>& modelLogicalToVisualMap = mImpl->mLogicalToVisualMap;
+
+  if( 0u == numberOfRuns )
+  {
+    modelVisualToLogicalMap.Clear();
+    modelLogicalToVisualMap.Clear();
+  }
+  else
+  {
+    const Length numberOfCharacters = mImpl->mText.Count();
+    modelVisualToLogicalMap.Resize( numberOfCharacters );
+    modelLogicalToVisualMap.Resize( numberOfCharacters );
+
+    CharacterIndex* modelVisualToLogicalMapBuffer = modelVisualToLogicalMap.Begin();
+    CharacterIndex* modelLogicalToVisualMapBuffer = modelLogicalToVisualMap.Begin();
+
+    CharacterIndex lastIndex = 0u;
+    for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex )
+    {
+      const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex );
+
+      if( lastIndex < bidiLineInfo.characterRun.characterIndex )
+      {
+        // Fill with the identity.
+        for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex )
+        {
+          *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
+        }
+      }
+
+      // Fill the conversion table of the run.
+      for( CharacterIndex index = 0u;
+           index < bidiLineInfo.characterRun.numberOfCharacters;
+           ++index, ++lastIndex )
+      {
+        *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index );
+      }
+    }
+
+    // Complete with the identity if there are some left to right characters after the last right to left.
+    for( ; lastIndex < numberOfCharacters; ++lastIndex )
+    {
+      *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
+    }
+
+    // Sets the logical to visual conversion map.
+    for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
+    {
+      *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index;
+    }
+  }
+}
+
+void LogicalModel::ReplaceVisualToLogicalMap( CharacterIndex characterIndex,
+                                              Length numberOfCharactersToRemove,
+                                              const BidirectionalLineInfoRun* const bidirectionalInfo,
+                                              Length numberOfCharactersToInsert )
+{
 }
 
 CharacterIndex LogicalModel::GetVisualCharacterIndex( CharacterIndex logicalCharacterIndex ) const
 {
-  return 0u;
+  if( 0u == mImpl->mLogicalToVisualMap.Count() )
+  {
+    // If there is no logical to visual info is because the whole text is left to right.
+    // Return the identity.
+    return logicalCharacterIndex;
+  }
+
+  return *( mImpl->mLogicalToVisualMap.Begin() + logicalCharacterIndex );
 }
 
 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const
 {
-  return 0u;
+  if( 0u == mImpl->mVisualToLogicalMap.Count() )
+  {
+    // If there is no visual to logical info is because the whole text is left to right.
+    // Return the identity.
+    return visualCharacterIndex;
+  }
+
+  return *( mImpl->mVisualToLogicalMap.Begin() + visualCharacterIndex );
 }
 
 void LogicalModel::GetLogicalToVisualMap( CharacterIndex* logicalToVisualMap,
                                           CharacterIndex characterIndex,
                                           Length numberOfCharacters ) const
 {
+  memcpy( logicalToVisualMap, mImpl->mLogicalToVisualMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
 }
 
 void LogicalModel::GetVisualToLogicalMap( CharacterIndex* visualToLogicalMap,
                                           CharacterIndex characterIndex,
                                           Length numberOfCharacters ) const
 {
+  memcpy( visualToLogicalMap, mImpl->mVisualToLogicalMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
 }
 
 LogicalModel::~LogicalModel()