KCode.kt revision 6047998943beebd81e0ae1068df39c0cbee38628
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *      http://www.apache.org/licenses/LICENSE-2.0
7 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS,
9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and
11 * limitations under the License.
12 */
13
14package android.databinding.tool.writer
15
16import android.databinding.tool.util.StringUtils
17import java.util.BitSet
18
19class KCode (private val s : String? = null){
20
21    private var sameLine = false
22
23    private val lineSeparator = StringUtils.LINE_SEPARATOR
24
25    class Appendix(val glue : String, val code : KCode)
26
27    private val nodes = arrayListOf<Any>()
28
29    companion object {
30        private val cachedIndentations = BitSet()
31        private val indentCache = arrayListOf<String>()
32        fun indent(n: Int): String {
33            if (cachedIndentations.get(n)) {
34                return indentCache[n]
35            }
36            val s = (0..n-1).fold(""){prev, next -> "$prev    "}
37            cachedIndentations.set(n, true )
38            while (indentCache.size <= n) {
39                indentCache.add("");
40            }
41            indentCache.set(n, s)
42            return s
43        }
44    }
45
46    fun isNull(kcode : KCode?) = kcode == null || (kcode.nodes.isEmpty() && (kcode.s == null || kcode.s.trim() == ""))
47
48    public fun tab(vararg codes : KCode?) : KCode {
49        codes.forEach { tab(it) }
50        return this
51    }
52
53    public fun tab(codes : Collection<KCode?> ) : KCode {
54        codes.forEach { tab(it) }
55        return this
56    }
57
58    fun tab(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
59        val c = KCode(s)
60        if (init != null) {
61            c.init()
62        }
63        return tab(c)
64    }
65
66    fun tab(c : KCode?) : KCode {
67        if (c == null || isNull(c)) {
68            return this
69        }
70        nodes.add(c)
71        return this
72    }
73
74    infix fun nl(c : KCode?) : KCode {
75        if (c == null || isNull(c)) {
76            return this
77        }
78        nodes.add(c)
79        c.sameLine = true
80        return this
81    }
82
83    fun nl(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
84        val c = KCode(s)
85        if (init != null) {
86            c.init()
87        }
88        return nl(c)
89    }
90
91    fun block(s : String, init : (KCode.() -> Unit)? = null) : KCode {
92        val c = KCode()
93        if (init != null) {
94            c.init()
95        }
96        return nl(s) {
97            app(" {")
98            tab(c)
99            nl("}")
100        }
101    }
102
103    fun app(glue : String = "", c : KCode?) : KCode {
104        if (isNull(c)) {
105            return this
106        }
107        nodes.add(Appendix(glue, c!!))
108        return this
109    }
110
111    infix fun app(s : String) : KCode {
112        val c = KCode(s)
113        return app("", c)
114    }
115
116    fun app(glue : String = "", s : String?, init : (KCode.() -> Unit)? = null) : KCode {
117        val c = KCode(s)
118        if (init != null) {
119            c.init()
120        }
121        return app(glue, c)
122    }
123
124
125    fun toS(n : Int, sb : StringBuilder) {
126        if (s != null) {
127            sb.append(s)
128        }
129        val newlineFirstNode = s != null || (nodes.isNotEmpty() && nodes.first() is Appendix)
130        var addedChild = false
131        nodes.forEach { when(it) {
132            is Appendix -> {
133                sb.append(it.glue)
134                it.code.toS(n, sb)
135            }
136            is KCode -> {
137                val childTab = n + (if(it.sameLine) 0 else 1)
138                if (addedChild || newlineFirstNode) {
139                    sb.append(lineSeparator)
140                }
141                if (!isNull(it)) { // avoid spaces for empty lines
142                    if (it.s != null && it.s.trim() != "") {
143                        sb.append("${indent(childTab)}")
144                    }
145                    it.toS(childTab, sb)
146                }
147                addedChild = true
148            }
149        } }
150
151    }
152
153    fun generate() : String {
154        val sb = StringBuilder()
155        toS(0, sb)
156        return sb.toString()
157    }
158}
159
160fun kcode(s : String?, init : (KCode.() -> Unit)? = null) : KCode {
161    val c = KCode(s)
162    if (init != null) {
163        c.init()
164    }
165    return c
166}