001/** 002 * 003 * Copyright 2014-2019 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.util; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022 023public class LazyStringBuilder implements Appendable, CharSequence { 024 025 private final List<CharSequence> list; 026 027 private String cache; 028 029 private void invalidateCache() { 030 cache = null; 031 } 032 033 public LazyStringBuilder() { 034 list = new ArrayList<>(20); 035 } 036 037 public LazyStringBuilder append(LazyStringBuilder lsb) { 038 list.addAll(lsb.list); 039 invalidateCache(); 040 return this; 041 } 042 043 @Override 044 public LazyStringBuilder append(CharSequence csq) { 045 assert csq != null; 046 list.add(csq); 047 invalidateCache(); 048 return this; 049 } 050 051 @Override 052 public LazyStringBuilder append(CharSequence csq, int start, int end) { 053 CharSequence subsequence = csq.subSequence(start, end); 054 list.add(subsequence); 055 invalidateCache(); 056 return this; 057 } 058 059 @Override 060 public LazyStringBuilder append(char c) { 061 list.add(Character.toString(c)); 062 invalidateCache(); 063 return this; 064 } 065 066 @Override 067 public int length() { 068 if (cache != null) { 069 return cache.length(); 070 } 071 int length = 0; 072 try { 073 for (CharSequence csq : list) { 074 length += csq.length(); 075 } 076 } 077 catch (NullPointerException npe) { 078 StringBuilder sb = safeToStringBuilder(); 079 throw new RuntimeException("The following LazyStringBuilder threw a NullPointerException: " + sb, npe); 080 } 081 return length; 082 } 083 084 @Override 085 public char charAt(int index) { 086 if (cache != null) { 087 return cache.charAt(index); 088 } 089 for (CharSequence csq : list) { 090 if (index < csq.length()) { 091 return csq.charAt(index); 092 } else { 093 index -= csq.length(); 094 } 095 } 096 throw new IndexOutOfBoundsException(); 097 } 098 099 @Override 100 public CharSequence subSequence(int start, int end) { 101 return toString().subSequence(start, end); 102 } 103 104 @Override 105 public String toString() { 106 if (cache == null) { 107 StringBuilder sb = new StringBuilder(length()); 108 for (CharSequence csq : list) { 109 sb.append(csq); 110 } 111 cache = sb.toString(); 112 } 113 return cache; 114 } 115 116 public StringBuilder safeToStringBuilder() { 117 StringBuilder sb = new StringBuilder(); 118 for (CharSequence csq : list) { 119 sb.append(csq); 120 } 121 return sb; 122 } 123 124 /** 125 * Get the List of CharSequences representation of this instance. The list is unmodifiable. If 126 * the resulting String was already cached, a list with a single String entry will be returned. 127 * 128 * @return a List of CharSequences representing this instance. 129 */ 130 public List<CharSequence> getAsList() { 131 if (cache != null) { 132 return Collections.singletonList((CharSequence) cache); 133 } 134 return Collections.unmodifiableList(list); 135 } 136}