Home | History | Annotate | Download | only in elements
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package parser.elements
     18 
     19 import lexer.ILexer
     20 import lexer.Token
     21 import lexer.TokenCategory
     22 import lexer.TokenGrammar
     23 import parser.peekPreviousToken
     24 import parser.peekToken
     25 import java.text.ParseException
     26 
     27 
     28 class DocAnnotationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
     29 
     30     lateinit var tag: TokenGrammar
     31     var arg: String? = null //some tags have arguments (eg. @param arg desc)
     32     lateinit var description: String
     33 
     34     init {
     35         parseTokens(scanTokens(iter))
     36         if (shouldResetIterator) resetIterator(iter)
     37     }
     38 
     39     private fun formatValue(tokens: List<Token>): String {
     40         return if (tokens.isEmpty()) {
     41             ""
     42         } else {
     43             tokens.map {
     44                 when (it.identifier) {
     45                     TokenGrammar.EMPTY_LINE -> "\n\n"
     46                     else -> it.value
     47                 }
     48             }
     49                     .joinToString(" ")
     50                     .let { ILexer.unpadDelimiters(it) }
     51         }
     52     }
     53 
     54     /**
     55      * Scan until: doc end token, empty line, another @param
     56      */
     57     override fun scanTokens(iter: ListIterator<Token>): List<Token> {
     58         val tokens = mutableListOf<Token>()
     59         var token: Token
     60 
     61         //depending how invoked, queue up doc annotation token
     62         if (peekToken(iter)?.identifier == TokenGrammar.AT) iter.next()
     63         if (peekPreviousToken(iter)?.category == TokenCategory.DocAnnotation) iter.previous()
     64 
     65         if (peekToken(iter)!!.category != TokenCategory.DocAnnotation)
     66             throw ParseException("Token sequence must begin with a DocAnnotation", this.indexStart)
     67 
     68         loop@ while (iter.hasNext()) {
     69             token = iter.next()
     70 
     71             when {
     72                 //descriptions don't span blank lines
     73                 token.identifier == TokenGrammar.EMPTY_LINE -> break@loop
     74 
     75                 //if doc block ends or found next annotation tag, back up and bail
     76                 token.identifier == TokenGrammar.DOC_END ||
     77                 token.identifier == TokenGrammar.AT && peekToken(iter)?.category == TokenCategory.DocAnnotation -> {
     78                     iter.previous()
     79                     break@loop
     80                 }
     81 
     82                 else -> tokens.add(token)
     83             }
     84         }
     85         return tokens
     86     }
     87 
     88     override fun parseTokens(tokens: List<Token>) {
     89         val iter = tokens.listIterator()
     90         assert(iter.hasNext())
     91         val token = iter.next()
     92 
     93         if (token.category != TokenCategory.DocAnnotation)
     94             throw ParseException("Invalid doc anootation tag: ${token.value}", this.indexStart)
     95 
     96         //annotation tag name (must be in TokenGrammar)
     97         this.tag = token.identifier
     98 
     99         //annotation tag can take an optional argument
    100         //so can return (preferred). TODO: check HALs if mandatory
    101         if (token.identifier == TokenGrammar.PARAM || token.identifier == TokenGrammar.RETURN) {
    102             if (iter.hasNext()) this.arg = iter.next().value
    103         }
    104 
    105         //the rest is annotation description
    106         val descTokens = mutableListOf<Token>()
    107         while (iter.hasNext()) {
    108             descTokens.add(iter.next())
    109         }
    110 
    111         this.description = if (descTokens.isEmpty()) {
    112             ""
    113         } else {
    114             descTokens.map { if (it.identifier == TokenGrammar.EMPTY_LINE) "\n\n" else it.value }
    115                     .joinToString(" ")
    116                     .let { ILexer.unpadDelimiters(it) }
    117         }
    118     }
    119 }