001/**
002 *
003 * Copyright 2014 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<CharSequence>(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        for (CharSequence csq : list) {
073            length += csq.length();
074        }
075        return length;
076    }
077
078    @Override
079    public char charAt(int index) {
080        if (cache != null) {
081            return cache.charAt(index);
082        }
083        for (CharSequence csq : list) {
084            if (index < csq.length()) {
085                return csq.charAt(index);
086            } else {
087                index -= csq.length();
088            }
089        }
090        throw new IndexOutOfBoundsException();
091    }
092
093    @Override
094    public CharSequence subSequence(int start, int end) {
095        return toString().subSequence(start, end);
096    }
097
098    @Override
099    public String toString() {
100        if (cache == null) {
101            StringBuilder sb = new StringBuilder(length());
102            for (CharSequence csq : list) {
103                sb.append(csq);
104            }
105            cache = sb.toString();
106        }
107        return cache;
108    }
109
110    /**
111     * Get the List of CharSequences representation of this instance. The list is unmodifiable. If
112     * the resulting String was already cached, a list with a single String entry will be returned.
113     *
114     * @return a List of CharSequences representing this instance.
115     */
116    public List<CharSequence> getAsList() {
117        if (cache != null) {
118            return Collections.singletonList((CharSequence) cache);
119        }
120        return Collections.unmodifiableList(list);
121    }
122}