Substring Error - Ending Position Out Of Bounds
Note that you do not need to skip an index between substrings. Your current pattern actually loses data. When you do substring(2001, 4000)
, you start at character #2002
. When you do substring(0, 2000)
, you stop at character #2000
(whose index was 1999
). So you lose character #2001
whose index is 2000
.
This can be demonstrated simply:
String text = '1234567890';
system.assertEquals('12345', text.substring(0, 5);
system.assertEquals('7890', text.substring(6, 10);
From the documentation (emphasis mine):
- substring(startIndex, endIndex)
Returns a new String that begins with the character at the specified zero-based startIndex and extends to the character at endIndex - 1.
If instead the substring extended to endIndex
, you would need to do substring(0, 1999)
, etc.
Solution
You need to split the data out into up to 16 chunks. To make it scalable, I would do something like the following in an Apex Class
and then call it from your trigger.
public with sharing class CaseServices
{
static final List<SObjectField> responseChunkFields = new List<SObjectField>
{
Case.Response_Part1__c, Case.Response_Part2__c,
Case.Response_Part3__c, Case.Response_Part4__c,
Case.Response_Part5__c, Case.Response_Part6__c,
Case.Response_Part7__c, Case.Response_Part8__c,
Case.Response_Part9__c, Case.Response_Part10__c,
Case.Response_Part11__c, Case.Response_Part12__c,
Case.Response_Part13__c, Case.Response_Part14__c,
Case.Response_Part15__c, Case.Response_Part16__c,
};
public static void chunkResponses(List<Case> records)
{
for (Case record : records) chunkResponse(record);
}
static void chunkResponse(Case record)
{
if (String.isBlank(record.Response__c)) return;
Integer length = record.Response__c.length;
Integer chunks = (Integer)(length/2000.0).round(RoundingMode.UP);
for (Integer i = 0; i < chunks; i++)
{
Integer j = (i == chunks - 1) ? length : 2000*(i+1);
String chunk = record.Response__c.substring(2000*i, j);
record.put(responseChunkFields[i], chunk);
}
}
}
As an alternative to using substring, you can also use Pattern/Matcher:
SObjectField[] fields = new SObjectField[] { // List your fields...
Case.Response_Part1__c, Case.Response_Part2__c,
Case.Response_Part3__c, Case.Response_Part4__c // ...
};
Pattern p = Pattern.compile('(?s).{1,2000}'); // Read up to 2k at a time
for(Case record: Trigger.new) {
if(record.Response__c == null || record.Response__c == Trigger.oldMap.get(record.Id).Response__c) {
continue;
}
Matcher m = p.matcher(c.Response__c);
for(Integer fieldToUse = 0; fieldToUse < fields.size() && m.find(); fieldToUse++) {
record.put(fields[fieldToUse], m.group(0));
}
}
This avoids substring exceptions and a particularly nasty set of if statements.
Reason :-
The length of string is less than 2000 which caused an error.
Resolution:-
Please use length() function of String class to solve the issue. Please use the below code:-
trigger UpdateCaseResponse on Case (before update) {
for(Case c: trigger.new ){
Integer resLength = c.Subject.length();
if (c.Response__c != null)
if(c.Response__c != Trigger.oldMap.get(c.Id).Response__c )
if( resLength <= 2000){
c.Response_Part1__c = c.Response__c.substring(0,resLength);
}
else if( resLength > 2000 && resLength <= 4000 ){
c.Response_Part1__c = c.Response__c.substring(0,2000);
c.Response_Part2__c = c.Response__c.substring(2001,resLength);
}
}
}