diff --git a/.idea/detekt.xml b/.idea/detekt.xml
new file mode 100644
index 0000000..d6df3a4
--- /dev/null
+++ b/.idea/detekt.xml
@@ -0,0 +1,9 @@
+
+
+
+ true
+ true
+ true
+ $PROJECT_DIR$/code_quality_tools/detekt.yml
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index fdc392f..b3e9cbd 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -16,5 +16,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 4b5664a..e125d8f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,9 +6,10 @@ buildscript {
}
plugins {
- id 'org.jetbrains.kotlin.jvm' version '1.4.30-M1'
+ id 'org.jetbrains.kotlin.jvm' version '1.4.21'
id 'info.solidsoft.pitest' version '1.5.2'
id 'maven'
+ id "io.gitlab.arturbosch.detekt" version "1.14.1"
}
group 'com.github.asm0dey'
@@ -16,10 +17,11 @@ version '1.0-SNAPSHOT'
repositories {
mavenCentral()
+ jcenter()
}
dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'com.fasterxml.woodstox:woodstox-core:6.2.3'
testImplementation "io.kotest:kotest-runner-junit5:$kotest_version"
testImplementation "ch.tutteli.atrium:atrium-infix-en_GB:$atrium_version"
@@ -45,3 +47,14 @@ pitest {
outputFormats = ['XML', 'HTML']
avoidCallsTo = ["kotlin.jvm.internal", "kotlinx.coroutines"]
}
+
+detekt {
+ parallel = false
+ config = files "code_quality_tools/detekt.yml"
+ reports {
+ xml { enabled = true }
+ html { enabled = true }
+ }
+}
+
+tasks.detekt.jvmTarget = "1.8"
\ No newline at end of file
diff --git a/code_quality_tools/detekt.yml b/code_quality_tools/detekt.yml
new file mode 100644
index 0000000..e9b9817
--- /dev/null
+++ b/code_quality_tools/detekt.yml
@@ -0,0 +1,617 @@
+comments:
+ active: true
+ AbsentOrWrongFileLicense:
+ active: false
+ licenseTemplateFile: 'license.template'
+ UndocumentedPublicClass:
+ active: true
+ searchInNestedClass: true
+ searchInInnerClass: true
+ searchInInnerObject: true
+ searchInInnerInterface: true
+ UndocumentedPublicFunction:
+ active: true
+ UndocumentedPublicProperty:
+ active: true
+
+complexity:
+ active: true
+ ComplexCondition:
+ active: true
+ threshold: 4
+ ComplexInterface:
+ active: true
+ threshold: 10
+ includeStaticDeclarations: false
+ includePrivateDeclarations: false
+ ComplexMethod:
+ active: true
+ threshold: 15
+ ignoreSingleWhenExpression: false
+ ignoreSimpleWhenEntries: false
+ ignoreNestingFunctions: false
+ nestingFunctions: [run, let, apply, with, also, use, forEach, isNotNull, ifNull]
+ LabeledExpression:
+ active: true
+ ignoredLabels: []
+ LargeClass:
+ active: true
+ threshold: 400
+ LongMethod:
+ active: true
+ threshold: 60
+ LongParameterList:
+ active: true
+ functionThreshold: 6
+ constructorThreshold: 7
+ ignoreDefaultParameters: false
+ ignoreDataClasses: true
+ ignoreAnnotated: []
+ MethodOverloading:
+ active: true
+ threshold: 10
+ NestedBlockDepth:
+ active: true
+ threshold: 4
+ ReplaceSafeCallChainWithRun:
+ active: true
+ StringLiteralDuplication:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ threshold: 3
+ ignoreAnnotation: true
+ excludeStringsWithLessThan5Characters: true
+ ignoreStringsRegex: '$^'
+ TooManyFunctions:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ thresholdInFiles: 30
+ thresholdInClasses: 11
+ thresholdInInterfaces: 11
+ thresholdInObjects: 11
+ thresholdInEnums: 11
+ ignoreDeprecated: false
+ ignorePrivate: false
+ ignoreOverridden: false
+
+coroutines:
+ active: true
+ GlobalCoroutineUsage:
+ active: true
+ RedundantSuspendModifier:
+ active: true
+ SuspendFunWithFlowReturnType:
+ active: true
+
+empty-blocks:
+ active: true
+ EmptyCatchBlock:
+ active: true
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ EmptyClassBlock:
+ active: true
+ EmptyDefaultConstructor:
+ active: true
+ EmptyDoWhileBlock:
+ active: true
+ EmptyElseBlock:
+ active: true
+ EmptyFinallyBlock:
+ active: true
+ EmptyForBlock:
+ active: true
+ EmptyFunctionBlock:
+ active: true
+ ignoreOverridden: false
+ EmptyIfBlock:
+ active: true
+ EmptyInitBlock:
+ active: true
+ EmptyKtFile:
+ active: true
+ EmptySecondaryConstructor:
+ active: true
+ EmptyTryBlock:
+ active: true
+ EmptyWhenBlock:
+ active: true
+ EmptyWhileBlock:
+ active: true
+
+exceptions:
+ active: true
+ ExceptionRaisedInUnexpectedLocation:
+ active: true
+ methodNames: [toString, hashCode, equals, finalize]
+ InstanceOfCheckForException:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ NotImplementedDeclaration:
+ active: true
+ PrintStackTrace:
+ active: true
+ RethrowCaughtException:
+ active: false
+ SwallowedException:
+ active: true
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ ThrowingExceptionFromFinally:
+ active: true
+ ThrowingExceptionInMain:
+ active: true
+ ThrowingExceptionsWithoutMessageOrCause:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ exceptions:
+ - IllegalArgumentException
+ - IllegalStateException
+ - IOException
+ ThrowingNewInstanceOfSameException:
+ active: true
+ TooGenericExceptionCaught:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ exceptionNames:
+ - ArrayIndexOutOfBoundsException
+ - Error
+ - Exception
+ - IllegalMonitorStateException
+ - NullPointerException
+ - IndexOutOfBoundsException
+ - RuntimeException
+ - Throwable
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ TooGenericExceptionThrown:
+ active: true
+ exceptionNames:
+ - Error
+ - Exception
+ - Throwable
+ - RuntimeException
+
+formatting:
+ active: true
+ android: false
+ autoCorrect: true
+ AnnotationOnSeparateLine:
+ active: false
+ autoCorrect: true
+ AnnotationSpacing:
+ active: true
+ autoCorrect: true
+ ArgumentListWrapping:
+ active: true
+ autoCorrect: true
+ ChainWrapping:
+ active: true
+ autoCorrect: true
+ CommentSpacing:
+ active: true
+ autoCorrect: true
+ EnumEntryNameCase:
+ active: true
+ autoCorrect: true
+ Filename:
+ active: true
+ FinalNewline:
+ active: true
+ autoCorrect: true
+ insertFinalNewLine: true
+ ImportOrdering:
+ active: true
+ autoCorrect: true
+ layout: 'idea'
+ MaximumLineLength:
+ active: true
+ maxLineLength: 140
+ ModifierOrdering:
+ active: true
+ autoCorrect: true
+ MultiLineIfElse:
+ active: true
+ autoCorrect: true
+ NoBlankLineBeforeRbrace:
+ active: true
+ autoCorrect: true
+ NoConsecutiveBlankLines:
+ active: true
+ autoCorrect: true
+ NoEmptyClassBody:
+ active: true
+ autoCorrect: true
+ NoEmptyFirstLineInMethodBlock:
+ active: false
+ autoCorrect: true
+ NoLineBreakAfterElse:
+ active: true
+ autoCorrect: true
+ NoLineBreakBeforeAssignment:
+ active: true
+ autoCorrect: true
+ NoMultipleSpaces:
+ active: true
+ autoCorrect: true
+ NoSemicolons:
+ active: true
+ autoCorrect: true
+ NoTrailingSpaces:
+ active: true
+ autoCorrect: true
+ NoUnitReturn:
+ active: true
+ autoCorrect: true
+ NoUnusedImports:
+ active: true
+ autoCorrect: true
+ NoWildcardImports:
+ active: true
+ PackageName:
+ active: true
+ autoCorrect: true
+ ParameterListWrapping:
+ active: true
+ autoCorrect: true
+ indentSize: 4
+ SpacingAroundColon:
+ active: true
+ autoCorrect: true
+ SpacingAroundComma:
+ active: true
+ autoCorrect: true
+ SpacingAroundCurly:
+ active: true
+ autoCorrect: true
+ SpacingAroundDot:
+ active: true
+ autoCorrect: true
+ SpacingAroundDoubleColon:
+ active: false
+ autoCorrect: true
+ SpacingAroundKeyword:
+ active: true
+ autoCorrect: true
+ SpacingAroundOperators:
+ active: true
+ autoCorrect: true
+ SpacingAroundParens:
+ active: true
+ autoCorrect: true
+ SpacingAroundRangeOperator:
+ active: true
+ autoCorrect: true
+ SpacingBetweenDeclarationsWithAnnotations:
+ active: false
+ autoCorrect: true
+ SpacingBetweenDeclarationsWithComments:
+ active: false
+ autoCorrect: true
+ StringTemplate:
+ active: true
+ autoCorrect: true
+
+naming:
+ active: true
+ ClassNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ classPattern: '[A-Z][a-zA-Z0-9]*'
+ ConstructorParameterNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ privateParameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ EnumNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
+ FunctionMaxLength:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ maximumFunctionNameLength: 30
+ FunctionMinLength:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ minimumFunctionNameLength: 3
+ FunctionNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ ignoreAnnotated: ['Composable']
+ FunctionParameterNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ InvalidPackageDeclaration:
+ active: true
+ excludes: ['*.kts']
+ rootPackage: ''
+ MatchingDeclarationName:
+ active: true
+ mustBeFirst: true
+ MemberNameEqualsClassName:
+ active: true
+ ignoreOverridden: true
+ NonBooleanPropertyPrefixedWithIs:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ ObjectPropertyNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ constantPattern: '[A-Za-z][_A-Za-z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
+ PackageNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
+ TopLevelPropertyNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ constantPattern: '[A-Z][_A-Z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
+ VariableMaxLength:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ maximumVariableNameLength: 64
+ VariableMinLength:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ minimumVariableNameLength: 2
+ VariableNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ variablePattern: '[a-z][A-Za-z0-9]*'
+ privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+
+performance:
+ active: true
+ ArrayPrimitive:
+ active: true
+ ForEachOnRange:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ SpreadOperator:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ UnnecessaryTemporaryInstantiation:
+ active: true
+
+potential-bugs:
+ active: true
+ Deprecation:
+ active: true
+ DuplicateCaseInWhenExpression:
+ active: true
+ EqualsAlwaysReturnsTrueOrFalse:
+ active: true
+ EqualsWithHashCodeExist:
+ active: true
+ ExplicitGarbageCollectionCall:
+ active: true
+ HasPlatformType:
+ active: false
+ IgnoredReturnValue:
+ active: true
+ restrictToAnnotatedMethods: true
+ returnValueAnnotations: ['*.CheckReturnValue', '*.CheckResult']
+ ImplicitDefaultLocale:
+ active: false
+ ImplicitUnitReturnType:
+ active: false
+ allowExplicitReturnType: true
+ InvalidRange:
+ active: true
+ IteratorHasNextCallsNextMethod:
+ active: true
+ IteratorNotThrowingNoSuchElementException:
+ active: true
+ LateinitUsage:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ excludeAnnotatedProperties: []
+ ignoreOnClassesPattern: ''
+ MapGetWithNotNullAssertionOperator:
+ active: false
+ NullableToStringCall:
+ active: true
+ RedundantElseInWhen:
+ active: true
+ UnconditionalJumpStatementInLoop:
+ active: true
+ UnnecessaryNotNullOperator:
+ active: true
+ UnnecessarySafeCall:
+ active: true
+ UnreachableCode:
+ active: true
+ UnsafeCallOnNullableType:
+ active: true
+ UnsafeCast:
+ active: true
+ UselessPostfixExpression:
+ active: true
+ WrongEqualsTypeParameter:
+ active: true
+
+style:
+ active: true
+ ClassOrdering:
+ active: false
+ CollapsibleIfStatements:
+ active: false
+ DataClassContainsFunctions:
+ active: false
+ conversionFunctionPrefix: 'to'
+ DataClassShouldBeImmutable:
+ active: false
+ EqualsNullCall:
+ active: true
+ EqualsOnSignatureLine:
+ active: false
+ ExplicitCollectionElementAccessMethod:
+ active: false
+ ExplicitItLambdaParameter:
+ active: false
+ ExpressionBodySyntax:
+ active: false
+ includeLineWrapping: false
+ ForbiddenComment:
+ active: true
+ values: ['TODO:', 'FIXME:', 'STOPSHIP:']
+ allowedPatterns: ''
+ ForbiddenImport:
+ active: false
+ imports: []
+ forbiddenPatterns: ''
+ ForbiddenMethodCall:
+ active: false
+ methods: ['kotlin.io.println', 'kotlin.io.print']
+ ForbiddenPublicDataClass:
+ active: true
+ excludes: ['**']
+ ignorePackages: ['*.internal', '*.internal.*']
+ ForbiddenVoid:
+ active: false
+ ignoreOverridden: false
+ ignoreUsageInGenerics: false
+ FunctionOnlyReturningConstant:
+ active: true
+ ignoreOverridableFunction: true
+ excludedFunctions: 'describeContents'
+ excludeAnnotatedFunction: ['dagger.Provides']
+ LibraryCodeMustSpecifyReturnType:
+ active: true
+ excludes: ['**']
+ LibraryEntitiesShouldNotBePublic:
+ active: true
+ excludes: ['**']
+ LoopWithTooManyJumpStatements:
+ active: true
+ maxJumpCount: 1
+ MagicNumber:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ ignoreNumbers: ['-1', '0', '1', '2']
+ ignoreHashCodeFunction: true
+ ignorePropertyDeclaration: false
+ ignoreLocalVariableDeclaration: false
+ ignoreConstantDeclaration: true
+ ignoreCompanionObjectPropertyDeclaration: true
+ ignoreAnnotation: false
+ ignoreNamedArgument: true
+ ignoreEnums: false
+ ignoreRanges: false
+ MandatoryBracesIfStatements:
+ active: false
+ MandatoryBracesLoops:
+ active: false
+ MaxLineLength:
+ active: true
+ maxLineLength: 120
+ excludePackageStatements: true
+ excludeImportStatements: true
+ excludeCommentStatements: false
+ MayBeConst:
+ active: true
+ ModifierOrder:
+ active: true
+ NestedClassesVisibility:
+ active: false
+ NewLineAtEndOfFile:
+ active: true
+ NoTabs:
+ active: false
+ OptionalAbstractKeyword:
+ active: true
+ OptionalUnit:
+ active: false
+ OptionalWhenBraces:
+ active: false
+ PreferToOverPairSyntax:
+ active: false
+ ProtectedMemberInFinalClass:
+ active: true
+ RedundantExplicitType:
+ active: false
+ RedundantVisibilityModifierRule:
+ active: false
+ ReturnCount:
+ active: true
+ max: 2
+ excludedFunctions: 'equals'
+ excludeLabeled: false
+ excludeReturnFromLambda: true
+ excludeGuardClauses: false
+ SafeCast:
+ active: true
+ SerialVersionUIDInSerializableClass:
+ active: false
+ SpacingBetweenPackageAndImports:
+ active: false
+ ThrowsCount:
+ active: true
+ max: 2
+ TrailingWhitespace:
+ active: false
+ UnderscoresInNumericLiterals:
+ active: false
+ acceptableDecimalLength: 5
+ UnnecessaryAbstractClass:
+ active: true
+ excludeAnnotatedClasses: ['dagger.Module']
+ UnnecessaryAnnotationUseSiteTarget:
+ active: false
+ UnnecessaryApply:
+ active: false
+ UnnecessaryInheritance:
+ active: true
+ UnnecessaryLet:
+ active: false
+ UnnecessaryParentheses:
+ active: false
+ UntilInsteadOfRangeTo:
+ active: false
+ UnusedImports:
+ active: false
+ UnusedPrivateClass:
+ active: true
+ UnusedPrivateMember:
+ active: false
+ allowedNames: '(_|ignored|expected|serialVersionUID)'
+ UseArrayLiteralsInAnnotations:
+ active: false
+ UseCheckNotNull:
+ active: false
+ UseCheckOrError:
+ active: false
+ UseDataClass:
+ active: false
+ excludeAnnotatedClasses: []
+ allowVars: false
+ UseEmptyCounterpart:
+ active: false
+ UseIfInsteadOfWhen:
+ active: false
+ UseRequire:
+ active: false
+ UseRequireNotNull:
+ active: false
+ UselessCallOnNotNull:
+ active: true
+ UtilityClassWithPublicConstructor:
+ active: true
+ VarCouldBeVal:
+ active: false
+ WildcardImport:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ excludeImports: ['java.util.*', 'kotlinx.android.synthetic.*']
diff --git a/src/main/kotlin/staks/CompoundHandler.kt b/src/main/kotlin/staks/CompoundHandler.kt
index e128807..c69356c 100644
--- a/src/main/kotlin/staks/CompoundHandler.kt
+++ b/src/main/kotlin/staks/CompoundHandler.kt
@@ -2,20 +2,24 @@ package staks
import javax.xml.stream.XMLEventReader
+/**
+ * Basic class which describes handler, which is built from another handlers.
+ */
abstract class CompoundHandler : Handler {
/**
- * Child handlers of current compound handler
+ * Child handlers of current compound handler.
*/
val children = hashSetOf>()
/**
- * every CompoundHandler should have access to XMLEventReader because it will potentially pass it to child for handling
- * Added for extensibility
+ * every CompoundHandler should have access to XMLEventReader because it will potentially pass it to child
+ * for handling.
+ * Added for extensibility.
*/
abstract val reader: XMLEventReader
/**
- * Every child should be registered for us to be able to pass handling to it if it should be done
+ * Every child should be registered for us to be able to pass handling to it if it should be done.
*/
fun > registerChild(child: R): R {
children.add(child)
@@ -23,8 +27,9 @@ abstract class CompoundHandler : Handler {
}
/**
- * Sometimes we need to wrap one delegate with another — for example if underlying delegate's data should be transformed somehow.
- * It should be achieved with decorate call
+ * Sometimes we need to wrap one delegate with another — for example if underlying delegate's data
+ * should be transformed somehow.
+ * It should be achieved with decorate call.
*/
fun > decorate(child: DECORATOR): DECORATOR {
children.remove(child.child)
@@ -33,12 +38,13 @@ abstract class CompoundHandler : Handler {
}
/**
- * Marks handler as optional. By default if result is `null` exception will be thrown. Optional handler will return null
+ * Marks handler as optional. By default if result is `null` exception will be thrown. Optional handler will
+ * return null.
*/
fun Handler.optional() = decorate(OptionalHandler(this))
/**
- * Converts underlying handler's result to int or throws Exception
+ * Converts underlying handler's result to int or throws Exception.
*/
fun Handler.int() = decorate(IntHandler(this))
diff --git a/src/main/kotlin/staks/Handler.kt b/src/main/kotlin/staks/Handler.kt
index 7588924..5f43a83 100644
--- a/src/main/kotlin/staks/Handler.kt
+++ b/src/main/kotlin/staks/Handler.kt
@@ -2,39 +2,43 @@ package staks
import javax.xml.stream.events.XMLEvent
+/**
+ * Basic interface for everything that should handle [XMLEvent]s.
+ */
interface Handler {
/**
- * Producer of value which was parser from XML
+ * Producer of value which was parser from XML.
*/
val value: () -> T
/**
- * true if should not be matched after first match
+ * true if should not be matched after first match.
*/
val isSingular: Boolean
/**
- * true if this concrete handler cares about these concrete event
+ * true if this concrete handler cares about these concrete event.
*/
fun matches(event: XMLEvent): Boolean
/**
- * Handle the XMLEvent and fill the result if needed
- * It's responsibility of handler to leave XMLEventReader in consistent state — for example wait for corresponding closing tag
+ * Handle the XMLEvent and fill the result if needed.
+ * It's responsibility of handler to leave XMLEventReader in consistent state — for example wait for
+ * corresponding closing tag.
* @throws IllegalArgumentException if can not build object
*/
fun process(ev: XMLEvent)
/**
* Clear the state: Most probably the handler is stateful, but it may be invoked several times if, for example,
- * it' inside list — the same handler will be invoked on every list invocation
+ * it' inside list — the same handler will be invoked on every list invocation.
*/
fun reset()
/**
- * call handler to obtain earlier calculated value
+ * call handler to obtain earlier calculated value.
* @throws IllegalArgumentException if can not parse data ot ir's not available
* @return T
*/
operator fun invoke() = value.invoke()
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/staks/ListHandler.kt b/src/main/kotlin/staks/ListHandler.kt
index 611cc9a..b2b0c3a 100644
--- a/src/main/kotlin/staks/ListHandler.kt
+++ b/src/main/kotlin/staks/ListHandler.kt
@@ -3,6 +3,9 @@ package staks
import javax.xml.stream.XMLEventReader
import javax.xml.stream.events.XMLEvent
+/**
+ * Handler which returns list of [T] or throws [IllegalArgumentException] if resulting list is empty.
+ */
class ListHandler(
private val tagName: String,
func: CompoundHandler<*>.() -> () -> T,
@@ -32,7 +35,7 @@ class ListHandler(
try {
result.add(builder())
return
- } catch (e: Exception) {
+ } catch (e: IllegalArgumentException) {
throw IllegalArgumentException("Unable to build info from $tagName", e)
}
}
@@ -47,4 +50,4 @@ class ListHandler(
override fun reset() {
result = ArrayList()
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/staks/SingleHandler.kt b/src/main/kotlin/staks/SingleHandler.kt
index da17ed6..13dba97 100644
--- a/src/main/kotlin/staks/SingleHandler.kt
+++ b/src/main/kotlin/staks/SingleHandler.kt
@@ -3,6 +3,9 @@ package staks
import javax.xml.stream.XMLEventReader
import javax.xml.stream.events.XMLEvent
+/**
+ * Handler, which returns first instance of type [T] ot throws exception if it wasn't found.
+ */
class SingleHandler(
private val tagName: String,
override val reader: XMLEventReader,
@@ -11,7 +14,7 @@ class SingleHandler(
CompoundHandler() {
private var result: T? = null
- val builder = func()
+ private val builder = func()
override val value: () -> T
get() = {
@@ -44,4 +47,4 @@ class SingleHandler(
override fun reset() {
result = null
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/staks/UnnamedListHandler.kt b/src/main/kotlin/staks/UnnamedListHandler.kt
index 3ba5b96..0b832f7 100644
--- a/src/main/kotlin/staks/UnnamedListHandler.kt
+++ b/src/main/kotlin/staks/UnnamedListHandler.kt
@@ -2,6 +2,9 @@ package staks
import javax.xml.stream.events.XMLEvent
+/**
+ * Handler which returns all instances of underlying child Handler.
+ */
class UnnamedListHandler(override val child: Handler) : ContainerHandler> {
private var result = arrayListOf()
diff --git a/src/main/kotlin/staks/base.kt b/src/main/kotlin/staks/base.kt
index d54578d..c086679 100644
--- a/src/main/kotlin/staks/base.kt
+++ b/src/main/kotlin/staks/base.kt
@@ -11,30 +11,133 @@ import javax.xml.stream.events.XMLEvent
import javax.xml.transform.Source
import javax.xml.stream.XMLStreamReader as XMLSR
+/**
+ * Main entry point for building objects.
+ * Usage example:
+ * ```kotlin
+ * val text = staks(inputStream){
+ * val txt = tagText("a");
+ * { txt() }
+ * }
+ * ```
+ * Here `text` will contain content of first met `a` tag in XML document. Parsing is lazy, so it will stop after
+ * first found `a` tag.
+ * @return [T]
+ */
fun staks(input: InputStream, func: (StaxBuilder.() -> () -> T)) = StaxBuilder(reader(input), func).process()
+/**
+ * Main entry point for building objects.
+ * Usage example:
+ * ```kotlin
+ * val text = staks(inputStream, encoding){
+ * val txt = tagText("a");
+ * { txt() }
+ * }
+ * ```
+ * Here `text` will contain content of first met `a` tag in XML document. Parsing is lazy, so it will stop after
+ * first found `a` tag.
+ * @return [T]
+ */
fun staks(input: InputStream, encoding: String, func: (StaxBuilder.() -> () -> T)) =
StaxBuilder(reader(input, encoding), func).process()
+/**
+ * Main entry point for building objects.
+ * Usage example:
+ * ```kotlin
+ * val text = staks(file){
+ * val txt = tagText("a");
+ * { txt() }
+ * }
+ * ```
+ * Here `text` will contain content of first met `a` tag in XML document. Parsing is lazy, so it will stop after
+ * first found `a` tag.
+ * @return [T]
+ */
fun staks(file: File, func: (StaxBuilder.() -> () -> T)) = StaxBuilder(reader(file), func).process()
+/**
+ * Main entry point for building objects.
+ * Usage example:
+ * ```kotlin
+ * val text = staks(source){
+ * val txt = tagText("a");
+ * { txt() }
+ * }
+ * ```
+ * Here `text` will contain content of first met `a` tag in XML document. Parsing is lazy, so it will stop after
+ * first found `a` tag.
+ * @param source [Source]
+ * @return [T]
+ */
+
fun staks(source: Source, func: (StaxBuilder.() -> () -> T)) = StaxBuilder(reader(source), func).process()
+/**
+ * Main entry point for building objects.
+ * Usage example:
+ * ```kotlin
+ * val text = staks(xmlStreamReader){
+ * val txt = tagText("a");
+ * { txt() }
+ * }
+ * ```
+ * Here `text` will contain content of first met `a` tag in XML document. Parsing is lazy, so it will stop after
+ * first found `a` tag.
+ * @return [T]
+ */
fun staks(reader: XMLSR, func: (StaxBuilder.() -> () -> T)) = StaxBuilder(reader(reader), func).process()
+/**
+ * Main entry point for building objects.
+ * Usage example:
+ * ```kotlin
+ * val text = staks(url){
+ * val txt = tagText("a");
+ * { txt() }
+ * }
+ * ```
+ * Here `text` will contain content of first met `a` tag in XML document. Parsing is lazy, so it will stop after
+ * first found `a` tag.
+ * @return [T]
+ */
fun staks(src: URL, func: (StaxBuilder.() -> () -> T)) = StaxBuilder(reader(src), func).process()
+/**
+ * Provider user with [XMLEventReader], suitable for usage with [StaxBuilder].
+ */
fun reader(file: File) = factory().createXMLEventReader(file)
+
+/**
+ * Provider user with [XMLEventReader], suitable for usage with [StaxBuilder].
+ */
fun reader(inputStream: InputStream) = factory().createXMLEventReader(inputStream)
+/**
+ * Provider user with [XMLEventReader], suitable for usage with [StaxBuilder].
+ */
fun reader(inputStream: InputStream, encoding: String) = factory().createXMLEventReader(inputStream, encoding)
+/**
+ * Provider user with [XMLEventReader], suitable for usage with [StaxBuilder].
+ */
fun reader(reader: XMLSR) = factory().createXMLEventReader(reader)
+/**
+ * Provider user with [XMLEventReader], suitable for usage with [StaxBuilder].
+ */
fun reader(source: Source) = factory().createXMLEventReader(source)
+/**
+ * Provider user with [XMLEventReader], suitable for usage with [StaxBuilder].
+ */
fun reader(source: URL) = factory().createXMLEventReader(source)
private fun factory(): XMLInputFactory2 =
(XMLInputFactory2.newInstance() as XMLInputFactory2).also { it.configureForConvenience() }
+/**
+ * Root [CompoundHandler] which creates reader and passes [XMLEvent] to underlying registered handlers and evetually
+ * returns built entity via calling `process method`. For usage examples @see [staks].
+ */
class StaxBuilder(override val reader: XMLEventReader, func: StaxBuilder.() -> () -> T) : CompoundHandler() {
- val builder = func()
+ private val builder = func()
- fun process(): T {
+ internal fun process(): T {
while (reader.hasNext()) {
val ev = reader.nextEvent()
for (it in children.filter { it.matches(ev) }) {
@@ -58,13 +161,13 @@ class StaxBuilder(override val reader: XMLEventReader, func: StaxBuilder.(
}
/**
- * Extracts data from tag assuming it contains only text or the first text met inside tag
+ * Extracts data from tag assuming it contains only text or the first text met inside tag.
*/
fun CompoundHandler<*>.tagText(tagName: String): TagTextHandler =
registerChild(TagTextHandler(tagName, reader))
/**
- * Extracts data from tag's attribute
+ * Extracts data from tag's attribute.
*/
fun CompoundHandler<*>.attribute(tagName: String, attributeName: String): AttrHandler =
registerChild(AttrHandler(tagName, attributeName))
@@ -79,13 +182,17 @@ fun CompoundHandler<*>.attribute(tagName: String, attributeName: String): AttrHa
* { data() }
* }
* ```
- * After parsing `names()` will contain list of contants of `inner-tag-name` tags
+ * After parsing `names()` will contain list of contants of `inner-tag-name` tags.
* @param tagName name of outer tag
* @param func description of inner tags
*/
fun CompoundHandler<*>.list(tagName: String, func: CompoundHandler<*>.() -> () -> T) =
registerChild(ListHandler(tagName, func, reader))
+/**
+ * Creates [UnnamedListHandler] from supplied handler.
+ * @param handler — underlying Handler with `isSingular` true. Behavior for `isSinguar` false is not defined.
+ */
fun CompoundHandler<*>.list(handler: Handler): Handler> = decorate(UnnamedListHandler(handler))
/**
@@ -107,4 +214,3 @@ fun CompoundHandler<*>.list(handler: Handler): Handler> = decorat
*/
fun CompoundHandler<*>.single(tagName: String, func: CompoundHandler<*>.() -> () -> T): SingleHandler =
registerChild(SingleHandler(tagName, reader, func))
-
diff --git a/src/main/kotlin/staks/delegates.kt b/src/main/kotlin/staks/delegates.kt
index e4dd321..558a767 100644
--- a/src/main/kotlin/staks/delegates.kt
+++ b/src/main/kotlin/staks/delegates.kt
@@ -3,7 +3,9 @@ package staks
import javax.xml.stream.XMLEventReader
import javax.xml.stream.events.XMLEvent
-
+/**
+ * Handler which converts underlying handler's String value to Int or throws [IllegalArgumentException].
+ */
class IntHandler(override val child: Handler) : DecoratingHandler {
override val value: () -> Int
get() = {
@@ -14,6 +16,11 @@ class IntHandler(override val child: Handler) : DecoratingHandler {
private var result: String? = null
override val value: () -> String
get() {
return {
- require(result != null) { "Unable to extract text from $tagName with attribute $attributeName, maybe it should be declared optional()" }
+ require(result != null) {
+ "Unable to extract text from $tagName with attribute $attributeName, " +
+ "maybe it should be declared optional()"
+ }
result!!
}
}
@@ -70,23 +82,38 @@ class AttrHandler(private val tagName: String, private val attributeName: String
}
}
-interface ContainerHandler:Handler{
+/**
+ * Basic [Handler] interface, which contains exactly on child Handler. Made for replacing one handler's result
+ * which another.
+ */
+interface ContainerHandler : Handler {
+ /**
+ * Child handler.
+ */
val child: Handler
}
+/**
+ * [ContainerHandler] implementaion, which passes everything necessary tto underlying [Handler], but explicitly knows
+ * that it may return another type.
+ */
interface DecoratingHandler : Handler, ContainerHandler