Android Question Crash on clicking selected CSBuilder text in EditText

RB Smissaert

Well-Known Member
Licensed User
Have a simple EditText that has a CSBuilder text in it (for SQL syntax highlighting). When I select a length of text (with the normal standard long click) and then do a click within that text I get a crash with this error message:

java.lang.IndexOutOfBoundsException: getChars (-1 ... 527) starts before 0
at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1314)
at android.text.SpannableStringBuilder.getChars(SpannableStringBuilder.java:1191)
at android.text.TextUtils.getChars(TextUtils.java:98)
at android.text.TextUtils.substring(TextUtils.java:289)
at android.view.inputmethod.BaseInputConnection.getSelectedText(BaseInputConnection.java:532)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:286)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:85)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

The problem is that I can't find where this error originates from. Running in debug mode does cause the same crash but it doesn't point to any B4A line. I tried stepping in debug mode by adding a Click event sub:

B4X:
Sub edtSQL_Click()
    Log("edtSQL_Click")
End Sub
But that doesn't lead me to the origin of the error either.

In case it matters the CSBuilder text does have clickable words, but the error also happens when clicking outside these clickable words. If formatting is turned off, so if the EditText holds plain text
then there is no error.

I am using the latest version of B4A and also the latest Android version.

RBS
 

Semen Matusovskiy

Well-Known Member
Licensed User
It's hard to say without code ... Meanwhile in case Of CSBuilder you mast avoid retrieving string using EditText1.Text.
In EditTextWrapper getText looks so
B4X:
public String getText()  {  return ((TextView)getObject()).getText().toString();  }
Try to use JavaObject
B4X:
Dim jo As JavaObject
Dim charsequenceFullText  As CSBuilder
...
jo = EditText1
charsequenceFullText = jo.RunMethod ("getText", Null)
If need to take substring use subSequence, for example
B4X:
Dim intSelectionStart As Int
Dim charsequenceTextBeforeSelection As CSBuilder

jo = charsequenceFullText
intSelectionStart = EditText1.SelectionStart
charsequenceTextBeforeSelection  = jo.RunMethod ("subSequence", Array (0, intSelectionStart))
 
Last edited:

RB Smissaert

Well-Known Member
Licensed User
It's hard to say without code ... Meanwhile in case Of CSBuilder you mast avoid retrieving string using EditText1.Text.
In EditTextWrapper getText looks so
B4X:
public String getText()  {  return ((TextView)getObject()).getText().toString();  }
Try to use JavaObject
B4X:
Dim jo As JavaObject
Dim charsequenceFullText  As CSBuilder
...
jo = EditText1
charsequenceFullText = jo.RunMethod ("getText", Null)
If need to take substring use subSequence, for example
B4X:
Dim intSelectionStart As Int
Dim charsequenceTextBeforeSelection As CSBuilder

jo = charsequenceFullText
intSelectionStart = EditText1.SelectionStart
charsequenceTextBeforeSelection  = jo.RunMethod ("subSequence", Array (0, intSelectionStart))
> It's hard to say without code

What is relevant code though? There is no code in the edtSQL_Click event.
Clicking in selected text should just alter the selection but I have no event code for that as far as I can see.

Will have a look at your first suggestion.
Subseqeuence I am using already.

RBS
 

Semen Matusovskiy

Well-Known Member
Licensed User
Probably, something wrong in internal structure of CSBuilder "strings".
I used CSBuilder in memory only, so I never converted them to sequence of bytes.
Meanwhile you mentioned "SQL". Means you read/write CharSequences. How ?
 

RB Smissaert

Well-Known Member
Licensed User
Probably, something wrong in internal structure of CSBuilder "strings".
I used CSBuilder in memory only, so I never converted them to sequence of bytes.
Meanwhile you mentioned "SQL". Means you read/write CharSequences. How ?
> converted them to sequence of bytes

Not sure what you mean with that. I don't do this.
The CSBuilder strings are used to colour syntax the SQL in that EditText view (edtSQL).

How do I check the internal structure of the CSBuilder strings?
They all show fine in the EditText.

RBS
 

Semen Matusovskiy

Well-Known Member
Licensed User
Where do you take strings, which you show in EditText ?
You create them in memory or you read them from external sources (for example, from DataBase) ?
In first case it's possible to expect correct internal structure. In second case - all is possible . That's why I thought about internal structure.
 
Last edited:

RB Smissaert

Well-Known Member
Licensed User
Where do you take strings, which you show in EditText ?
You create them in memory or you read them from external sources (for example, from DataBase) ?
In first case internal structure should be correct anyway. In second - all is possible . That's why I thought about internal structure.
> Where do you take strings, which you show in EditText ?

They are either directly typed in by the user or they are loaded from database where they are stored as simple strings.
Well, I thought it was simple text, but looking at your previous post maybe not:

B4X:
strSQL = edtSQL.Text
SaveSQL(strSQL, strName) 'save to DB
Will have a look at your Java method to get the pure text.

RBS
 

RB Smissaert

Well-Known Member
Licensed User
> Where do you take strings, which you show in EditText ?

They are either directly typed in by the user or they are loaded from database where they are stored as simple strings.
Well, I thought it was simple text, but looking at your previous post maybe not:

B4X:
strSQL = edtSQL.Text
SaveSQL(strSQL, strName) 'save to DB
Will have a look at your Java method to get the pure text.

RBS
Are you saying that instead of:

B4X:
strSQL = edtSQL.Text
I should do:

B4X:
Sub GetCSBuilderText(oView As View) As String
 
 Dim jo As JavaObject
 Dim charsequenceFullText  As CSBuilder

 jo = oView
 charsequenceFullText = jo.RunMethod ("getText", Null)
 Return charsequenceFullText.ToString

End Sub
And then:

B4X:
strSQL = GetCSBuilderText(edtSQL)
RBS
 

Erel

Administrator
Staff member
Licensed User
Meanwhile in case Of CSBuilder you mast avoid retrieving string using EditText1.Text.
That's not correct.

Probably, something wrong in internal structure of CSBuilder "strings".
That's probably not correct as well.

Where do you take strings, which you show in EditText ?
You create them in memory or you read them from external sources (for example, from DataBase) ?
In first case it's possible to expect correct internal structure. In second case - all is possible . That's why I thought about internal structure.
I don't understand this one.

Lets start from scratch.

Where is the code that builds the CSBuilder?
 

Semen Matusovskiy

Well-Known Member
Licensed User
Erel --

About your comment "That's not correct" relative
Meanwhile in case Of CSBuilder you mast avoid retrieving string using EditText1.Text.
Try Dim cs As CSBuilder = edittext1.Text
Compiler will wrote:
src\b4a\example\main.java:346: error: incompatible types: String cannot be converted to SpannableStringBuilder
_cs.setObject((android.text.SpannableStringBuilder)(mostCurrent._edittext1.getText()));
And what to do ? If to use Dim stringText As String = edittext1.Text, formatting will be lost
 

RB Smissaert

Well-Known Member
Licensed User
The best you can do is to create a small program that creates the problematic CSBuilder and upload it.
Solved the problem, but not really found the cause of it.
The SQL string to be put in the EditText (edtSQL) didn't actually come directly from the database but from a TableView control (Table CustomView by Erel/Klaus).

This is the code retrieving the SQL text (pure text, not CSBuilder text) and dumping it to the EditText:

B4X:
Sub btnLoadSQL_Click() 'runs from list of saved SQL
 
 Dim strSQL As String
 Dim strSQL2 As String
 Dim cs As CSBuilder
 
 'iSavedSQLRow is produced by table row-click event
 tSavedSQL.Name = tblSavedSQL.GetValue(0, iSavedSQLRow)
 General.RunLog("btnLoadSQL_Click, tSavedSQL.Name: " & tSavedSQL.Name)
 
 'this is fine, no error on clicking selected text
'--------------------------------------------------
 strSQL = General.SQL1.ExecQuerySingleResult2("SELECT SQL FROM SQL WHERE NAME = ?", Array As String(tSavedSQL.Name))
 General.RunLog("btnLoadSQL_Click, strSQL:")
 General.RunLog(strSQL)
 
 'this one will cause error on clicking selected text (only if CSBuilder text)
'---------------------------------------------------------------------------------
 strSQL2 = tblSavedSQL.GetValue(3, iSavedSQLRow)
 General.RunLog("btnLoadSQL_Click, strSQL2:")
 General.RunLog(strSQL2)
 
 'this will produce True, so strSQL = strSQL2
 General.RunLog("btnLoadSQL_Click, strSQL = strSQL2: " & General.Boolean2String(strSQL = strSQL2))
 
 lblOpenedSQL.Text = tSavedSQL.Name
 
 If bNoSQLFormattingAtAll Then
  edtSQL.Text = strSQL
 Else
  cs = FormatSQL(strSQL, Null, -1, -1)
  bNoSQLFormatting = True
  edtSQL.Text = cs
  bNoSQLFormatting = False
 End If

 GotoPanel(ePanelType.SQLEdit, False, True)
 
End Sub
Now I thought let's get the SQL string directly from the DB instead and see if that fixes the problem.
Strangely enough it does, although as far as I can see the 2 retrieved strings are exactly the same as
demonstrated in the above code.
I don't understand this at all. I will examine the 2 strings more thoroughly, byte for byte, but as far as I know
if strSQL = strSQL2 then all the bytes must be the same as well.


RBS
 

RB Smissaert

Well-Known Member
Licensed User
Can you post the logs?
If the strings are equal then the problem is somewhere else.

This is not needed:
B4X:
 bNoSQLFormatting = True
  edtSQL.Text = cs
  bNoSQLFormatting = False
I posted the logs already. Or do you want something else?
I know it is very strange, but for sure swapping strSQL and strSQL2 will clear or cause the mentioned problem.

RBS
 

RB Smissaert

Well-Known Member
Licensed User
All I can say from the information that I see is that if the strings are equal then it doesn't matter where they come from.
Yes, agree with that and just noticed another crash, but it is a lot less frequent.
Will see if I can reproduce in a small demo.

RBS
 

RB Smissaert

Well-Known Member
Licensed User
That's not correct.


That's probably not correct as well.


I don't understand this one.

Lets start from scratch.

Where is the code that builds the CSBuilder?

This is the code that builds the CSBuilder:

B4X:
Sub InitSQLColours
 SQLColours(0) = Colors.RGB(0, 0, 0)
 SQLColours(1) = Colors.RGB(0, 0, 220)
 SQLColours(2) = Colors.RGB(200, 60, 0)
 SQLColours(3) = Colors.RGB(0, 0, 220)
 SQLColours(4) = Colors.RGB(220, 0, 0)
 SQLColours(5) = Colors.RGB(220, 0, 0)
 SQLColours(6) = Colors.RGB(0, 80, 255)
 SQLColours(7) = Colors.RGB(128, 0, 255)
 SQLColours(8) = Colors.RGB(0, 200, 0) 'literal
 SQLColours(9) = Colors.RGB(128, 128, 0) 'for brackets
 SQLColours(10) = Colors.RGB(140, 100, 0) 'for table names
 SQLColours(11) = Colors.RGB(0, 140, 100) 'for view names
 SQLColours(12) = Colors.RGB(100, 32, 200) 'for SQL replace strings
 SQLColours(13) = Colors.RGB(100, 32, 200) 'for variable SQL replace strings
 SQLColours(14) = Colors.RGB(100, 32, 200) 'for custom function, for now only SD2XLD and CDMinusXDays
End Sub

Sub FormatSQL(strSQL As String) As CSBuilder

 Dim i As Int
 Dim cs As CSBuilder
 Dim arrSQLWords() As SQLWord
 Dim UB As Int
 Dim strWordLower As String
 Dim tf As Typeface

 tf = Typeface.CreateNew(Typeface.MONOSPACE, Typeface.STYLE_ITALIC)

 cs.Initialize.Color (Colors.Black)

 If bNoSQLFormattingAtAll Or bNoSQLFormatting Then
  Return cs
 End If

 'note that we shouldn't remove comments here as otherwise the SQL in the editor will change!
 '-------------------------------------------------------------------------------------------
 'this is very quick, about 1 msecs
 arrSQLWords = GetSQLWords(strSQL, 0, "", False, False)
 ' For i = 0 To arrSQLWords.Length - 1
 '  General.RunLog("FormatSQL, Word: |" & arrSQLWords(i).strWord & "|")
 ' Next

 If arrSQLWords.Length = 0 Then
  Return cs
 End If

 General.StartSW (3)

 UB = arrSQLWords.Length - 1

 For i = 0 To UB
  'General.RunLog("FormatSQL, Word: " & arrSQLWords(i).strWord)

  If arrSQLWords(i).bWord Then
   If arrSQLWords(i).bLiteral Or arrSQLWords(i).bCommentStart Or arrSQLWords(i).bComment Or arrSQLWords(i).bCommentEnd Then
    'green for literal string or comment
    '-----------------------------------
    If arrSQLWords(i).bLiteral Or arrSQLWords(i).bComment Then
     cs.Color(SQLColours(8)).Append(arrSQLWords(i).strWord).Pop
    Else
     cs.Color(SQLColours(4)).Append(arrSQLWords(i).strWord).Pop
    End If
   Else    'If arrSQLWords(i).bLiteral
    strWordLower = arrSQLWords(i).strWord.ToLowerCase
    If mapSQLWordTypes.ContainsKey(strWordLower) Then
     Select Case mapSQLWordTypes.Get(strWordLower)
      Case 1
       'deal with SQL key word in square brackets or double quotes
       'no need to check previous byte as if not matching, SQL will fail Prepare
       '------------------------------------------------------------------------
       If arrSQLWords(i).lPos > 0 And _
                            (arrSQLWords(i).btNextByte = 34 Or arrSQLWords(i).btNextByte = 93) Then
        cs.Append (arrSQLWords(i).strWord)
       Else
        cs.Bold.Color(SQLColours(1)).Append(arrSQLWords(i).strWord).Pop.Pop
       End If
      Case 2
       cs.Bold.Color(SQLColours(2)).Append(arrSQLWords(i).strWord).Pop.Pop
      Case 3
       cs.Color(SQLColours(3)).Append(arrSQLWords(i).strWord).Pop
      Case 4
       cs.Color(SQLColours(4)).Append(arrSQLWords(i).strWord).Pop
      Case 5
       cs.Color(SQLColours(4)).Append(arrSQLWords(i).strWord).Pop
      Case 6
       cs.Color(SQLColours(6)).Append(arrSQLWords(i).strWord).Pop
      Case 7
       cs.Color(SQLColours(7)).Append(arrSQLWords(i).strWord).Pop
      Case 10
       'tables, take care for fields that have same name as a table or view
       '-------------------------------------------------------------------
       If arrSQLWords(i).strPreviousWord.ToLowerCase = "from" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "join" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "table" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "into" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "update" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "exists" Then
        cs.Underline.Bold.Color(SQLColours(11)).Clickable("Table", _
                                       arrSQLWords(i).strWord).Append(arrSQLWords(i).strWord).Pop.Pop.Pop.Pop
       Else
        cs.Append (arrSQLWords(i).strWord)
       End If
      Case 11
       'views, take care for fields that have same name as a table or view
       '-----------------------------------------------------------------
       If arrSQLWords(i).strPreviousWord.ToLowerCase = "from" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "join" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "view" Then
        cs.Underline.Bold.Color(SQLColours(11)).Clickable("View", _
                                      arrSQLWords(i).strWord).Append(arrSQLWords(i).strWord).Pop.Pop.Pop.Pop
       Else
        cs.Append (arrSQLWords(i).strWord)
       End If
      Case 12
       'SQL replace strings
       cs.Underline.Color(SQLColours(12)).Clickable("Shortcut", _
                                     arrSQLWords(i).strWord).Append(arrSQLWords(i).strWord).Pop.Pop.Pop
      Case 13
       'SQL replace strings, based on variable
       cs.Typeface(tf).Underline.Color(SQLColours(13)).Clickable("VariableShortcut", _
                                     arrSQLWords(i).strWord).Append(arrSQLWords(i).strWord).Pop.Pop.Pop.Pop
      Case 14
       'custom SQL functions for now only SD2XLD
       cs.Typeface(tf).Color(SQLColours(6)).Append(arrSQLWords(i).strWord).Pop.Pop
     End Select
    Else    'If  mapSQLWordTypes.ContainsKey(strWordLower)
     'cs.Color(SQLColours(0)).Append(arrSQLWords(i).strWord).Pop
     cs.Append (arrSQLWords(i).strWord)
    End If    'If  mapSQLWordTypes.ContainsKey(strWordLower)
   End If    'If arrSQLWords(i).bLiteral
  Else    'If arrSQLWords(i).bWord
   If arrSQLWords(i).strWord.Trim.Length = 0 Then
    cs.Append (arrSQLWords(i).strWord)
   Else
    'only problem with this is that commas and % will be done like brackets
    cs.Color(SQLColours(9)).Append(arrSQLWords(i).strWord).Pop
   End If
  End If    'If arrSQLWords(i).bWord
 Next

 cs.PopAll

 cs.EnableClickEvents (edtSQL)
 'formatting takes a bit longer about 7 msecs for the Sudoku SQL
 '--------------------------------------------------------------
 'General.RunLog ("FormatSQL, time Formatting: " & General.StopSW(3) & " msecs")
 Return cs

End Sub
There is a lot of other code involved, but maybe somebody can spot a possible problem here.


RBS
 
Top