2020import java .util .function .Consumer ;
2121import java .util .function .Function ;
2222import java .util .function .Supplier ;
23- import java .util .regex .Pattern ;
2423
2524/**
2625 * Helper class for generating code.
@@ -609,7 +608,6 @@ public abstract class AbstractCodeWriter<T extends AbstractCodeWriter<T>> {
609608 '_' ,
610609 '`' };
611610
612- private static final Pattern LINES = Pattern .compile ("\\ r?\\ n" );
613611 private static final Map <Character , BiFunction <Object , String , String >> DEFAULT_FORMATTERS = MapUtils .of (
614612 'L' ,
615613 (s , i ) -> formatLiteral (s ),
@@ -621,7 +619,6 @@ public abstract class AbstractCodeWriter<T extends AbstractCodeWriter<T>> {
621619 private boolean trailingNewline = true ;
622620 private int trimBlankLines = -1 ;
623621 private boolean enableStackTraceComments ;
624- private final List <String > deferredPlaceholders = new ArrayList <>();
625622 private final List <Supplier <String >> deferredSuppliers = new ArrayList <>();
626623 private int deferredCounter = 0 ;
627624
@@ -788,47 +785,66 @@ public char getExpressionStart() {
788785 public String toString () {
789786 String result = currentState .toString ();
790787
791- // Resolve any deferred values.
792- if (!deferredPlaceholders .isEmpty ()) {
793- // Find all sentinel positions, then resolve in a single linear pass.
794- List <int []> positions = new ArrayList <>();
795- for (int i = 0 ; i < deferredPlaceholders .size (); i ++) {
796- String key = deferredPlaceholders .get (i );
797- int fromIndex = 0 ;
798- while ((fromIndex = result .indexOf (key , fromIndex )) != -1 ) {
799- positions .add (new int [] {fromIndex , i , key .length ()});
800- fromIndex += key .length ();
788+ // Resolve any deferred values with a single linear scan.
789+ if (deferredCounter > 0 ) {
790+ StringBuilder resolved = new StringBuilder (result .length ());
791+ int pos = 0 ;
792+ int fromIndex = 0 ;
793+ int len = result .length ();
794+ while (fromIndex < len ) {
795+ if (result .charAt (fromIndex ) == '\0' && fromIndex + 1 < len && result .charAt (fromIndex + 1 ) == '\0' ) {
796+ int idStart = fromIndex + 2 ;
797+ int end = result .indexOf ("\0 \0 " , idStart );
798+ if (end != -1 ) {
799+ int id = 0 ;
800+ for (int k = idStart ; k < end ; k ++) {
801+ id = id * 10 + (result .charAt (k ) - '0' );
802+ }
803+ int keyLen = end + 2 - fromIndex ;
804+ resolved .append (result , pos , fromIndex );
805+ resolved .append (deferredSuppliers .get (id ).get ());
806+ fromIndex += keyLen ;
807+ pos = fromIndex ;
808+ continue ;
809+ }
801810 }
811+ fromIndex ++;
802812 }
803- if (!positions .isEmpty ()) {
804- positions .sort ((a , b ) -> Integer .compare (a [0 ], b [0 ]));
805- StringBuilder resolved = new StringBuilder (result .length ());
806- int pos = 0 ;
807- for (int [] entry : positions ) {
808- resolved .append (result , pos , entry [0 ]);
809- resolved .append (deferredSuppliers .get (entry [1 ]).get ());
810- pos = entry [0 ] + entry [2 ];
811- }
812- resolved .append (result , pos , result .length ());
813+ if (pos > 0 ) {
814+ resolved .append (result , pos , len );
813815 result = resolved .toString ();
814816 }
815817 }
816818
817819 // Trim excessive blank lines.
818820 if (trimBlankLines > -1 ) {
819821 StringBuilder builder = new StringBuilder (result .length ());
820- String [] lines = LINES .split (result );
821822 int blankCount = 0 ;
822-
823- for (String line : lines ) {
824- if (!StringUtils .isBlank (line )) {
825- builder .append (line ).append (currentState .newline );
823+ int start = 0 ;
824+ int len = result .length ();
825+ int lastNonBlankEnd = 0 ;
826+
827+ while (start < len ) {
828+ int newlineIdx = result .indexOf ('\n' , start );
829+ int lineEnd = (newlineIdx == -1 ) ? len : newlineIdx ;
830+ // Handle \r\n
831+ int lineContentEnd = (lineEnd > start && result .charAt (lineEnd - 1 ) == '\r' )
832+ ? lineEnd - 1
833+ : lineEnd ;
834+
835+ boolean blank = StringUtils .isBlank (result , start , lineContentEnd );
836+ if (!blank ) {
837+ builder .append (result , start , lineContentEnd ).append (currentState .newline );
826838 blankCount = 0 ;
839+ lastNonBlankEnd = builder .length ();
827840 } else if (blankCount ++ < trimBlankLines ) {
828- builder .append (line ).append (currentState .newline );
841+ builder .append (result , start , lineContentEnd ).append (currentState .newline );
829842 }
843+
844+ start = (newlineIdx == -1 ) ? len : newlineIdx + 1 ;
830845 }
831846
847+ builder .setLength (lastNonBlankEnd );
832848 result = builder .toString ();
833849 }
834850
@@ -1804,7 +1820,6 @@ public final String format(Object content, Object... args) {
18041820 */
18051821 protected final String defer (Supplier <String > supplier ) {
18061822 String id = "\u0000 \u0000 " + (deferredCounter ++) + "\u0000 \u0000 " ;
1807- deferredPlaceholders .add (id );
18081823 deferredSuppliers .add (supplier );
18091824 return id ;
18101825 }
0 commit comments