add stylecheck and spotbugs
This commit is contained in:
parent
828cec3f1e
commit
338d172893
6 changed files with 1168 additions and 639 deletions
|
|
@ -8,6 +8,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
// Apply the application plugin to add support for building a CLI application in Java.
|
// Apply the application plugin to add support for building a CLI application in Java.
|
||||||
application
|
application
|
||||||
|
checkstyle
|
||||||
id("com.gradleup.shadow") version "9.0.0-beta12"
|
id("com.gradleup.shadow") version "9.0.0-beta12"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,6 +17,13 @@ repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sourceSets { main { java { srcDirs("src/main/java") } } }
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
toolVersion = "10.12.4"
|
||||||
|
configFile = file("./checkstyle.xml")
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Use JUnit Jupiter for testing.
|
// Use JUnit Jupiter for testing.
|
||||||
testImplementation(libs.junit.jupiter)
|
testImplementation(libs.junit.jupiter)
|
||||||
|
|
|
||||||
447
app/checkstyle.xml
Normal file
447
app/checkstyle.xml
Normal file
|
|
@ -0,0 +1,447 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||||
|
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Checkstyle configuration that checks the Google coding conventions from Google Java Style
|
||||||
|
that can be found at https://google.github.io/styleguide/javaguide.html
|
||||||
|
|
||||||
|
Checkstyle is very configurable. Be sure to read the documentation at
|
||||||
|
http://checkstyle.org (or in your downloaded distribution).
|
||||||
|
|
||||||
|
To completely disable a check, just comment it out or delete it from the file.
|
||||||
|
To suppress certain violations please review suppression filters.
|
||||||
|
|
||||||
|
Authors: Max Vetrenko, Mauryan Kansara, Ruslan Diachenko, Roman Ivanov.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name="Checker">
|
||||||
|
|
||||||
|
<property name="charset" value="UTF-8"/>
|
||||||
|
|
||||||
|
<property name="severity" value="${org.checkstyle.google.severity}" default="warning"/>
|
||||||
|
|
||||||
|
<property name="fileExtensions" value="java, properties, xml"/>
|
||||||
|
<!-- Excludes all 'module-info.java' files -->
|
||||||
|
<!-- See https://checkstyle.org/filefilters/index.html -->
|
||||||
|
<!-- <module name="BeforeExecutionExclusionFileFilter"> -->
|
||||||
|
<!-- <property name="fileNamePattern" value=""/> -->
|
||||||
|
<!-- </module> -->
|
||||||
|
<module name="SuppressWarningsFilter"/>
|
||||||
|
|
||||||
|
<!-- https://checkstyle.org/filters/suppressionfilter.html -->
|
||||||
|
<module name="SuppressionFilter">
|
||||||
|
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
|
||||||
|
default="checkstyle-suppressions.xml" />
|
||||||
|
<property name="optional" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- https://checkstyle.org/filters/suppresswithnearbytextfilter.html -->
|
||||||
|
<module name="SuppressWithNearbyTextFilter">
|
||||||
|
<property name="nearbyTextPattern"
|
||||||
|
value="CHECKSTYLE.SUPPRESS\: (\w+) for ([+-]\d+) lines"/>
|
||||||
|
<property name="checkPattern" value="$1"/>
|
||||||
|
<property name="lineRange" value="$2"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- Checks for whitespace -->
|
||||||
|
<!-- See http://checkstyle.org/checks/whitespace/index.html -->
|
||||||
|
<module name="FileTabCharacter">
|
||||||
|
<property name="eachLine" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LineLength">
|
||||||
|
<property name="fileExtensions" value="java"/>
|
||||||
|
<property name="max" value="100"/>
|
||||||
|
<property name="ignorePattern"
|
||||||
|
value="^package.*|^import.*|href\s*=\s*"[^"]*"|http://|https://|ftp://"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
<module name="OuterTypeFilename"/>
|
||||||
|
<module name="IllegalTokenText">
|
||||||
|
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||||
|
<property name="format"
|
||||||
|
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||||
|
<property name="message"
|
||||||
|
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidEscapedUnicodeCharacters">
|
||||||
|
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||||
|
<property name="allowByTailComment" value="true"/>
|
||||||
|
<property name="allowNonPrintableEscapes" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidStarImport"/>
|
||||||
|
<module name="OneTopLevelClass"/>
|
||||||
|
<module name="NoLineWrap">
|
||||||
|
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
|
||||||
|
</module>
|
||||||
|
<module name="NeedBraces">
|
||||||
|
<property name="tokens"
|
||||||
|
value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
|
||||||
|
</module>
|
||||||
|
<module name="LeftCurly">
|
||||||
|
<property name="id" value="LeftCurlyEol"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
|
||||||
|
INTERFACE_DEF, LAMBDA, LITERAL_CATCH,
|
||||||
|
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
|
||||||
|
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
|
||||||
|
OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="LeftCurly">
|
||||||
|
<property name="id" value="LeftCurlyNl"/>
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="LITERAL_CASE, LITERAL_DEFAULT"/>
|
||||||
|
</module>
|
||||||
|
<module name="SuppressionXpathSingleFilter">
|
||||||
|
<!-- LITERAL_CASE, LITERAL_DEFAULT are reused in SWITCH_RULE -->
|
||||||
|
<property name="id" value="LeftCurlyNl"/>
|
||||||
|
<property name="query" value="//SWITCH_RULE/SLIST"/>
|
||||||
|
</module>
|
||||||
|
<module name="RightCurly">
|
||||||
|
<property name="id" value="RightCurlySame"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_IF, LITERAL_ELSE,
|
||||||
|
LITERAL_DO"/>
|
||||||
|
</module>
|
||||||
|
<module name="SuppressionXpathSingleFilter">
|
||||||
|
<property name="id" value="RightCurlySame"/>
|
||||||
|
<property name="query" value="//RCURLY[parent::SLIST[parent::LITERAL_CATCH
|
||||||
|
and not(parent::LITERAL_CATCH/following-sibling::*)]]"/>
|
||||||
|
</module>
|
||||||
|
<module name="RightCurly">
|
||||||
|
<property name="id" value="RightCurlyAlone"/>
|
||||||
|
<property name="option" value="alone"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
|
||||||
|
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
|
||||||
|
COMPACT_CTOR_DEF, LITERAL_SWITCH, LITERAL_CASE, LITERAL_FINALLY,
|
||||||
|
LITERAL_CATCH"/>
|
||||||
|
</module>
|
||||||
|
<module name="SuppressionXpathSingleFilter">
|
||||||
|
<!-- suppression is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
|
||||||
|
<property name="id" value="RightCurlyAlone"/>
|
||||||
|
<property name="query" value="//RCURLY[parent::SLIST[count(./*)=1
|
||||||
|
and not(parent::LITERAL_CATCH)]
|
||||||
|
or (preceding-sibling::*[last()][self::LCURLY]
|
||||||
|
and not(parent::SLIST/parent::LITERAL_CATCH))
|
||||||
|
or (parent::SLIST/parent::LITERAL_CATCH
|
||||||
|
and parent::SLIST/parent::LITERAL_CATCH/following-sibling::*)]"/>
|
||||||
|
</module>
|
||||||
|
<module name="WhitespaceAfter">
|
||||||
|
<property name="tokens"
|
||||||
|
value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_RETURN,
|
||||||
|
LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, LITERAL_FINALLY, DO_WHILE, ELLIPSIS,
|
||||||
|
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_CATCH, LAMBDA,
|
||||||
|
LITERAL_YIELD, LITERAL_CASE, LITERAL_WHEN"/>
|
||||||
|
</module>
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<property name="allowEmptyConstructors" value="true"/>
|
||||||
|
<property name="allowEmptyLambdas" value="true"/>
|
||||||
|
<property name="allowEmptyMethods" value="true"/>
|
||||||
|
<property name="allowEmptyTypes" value="true"/>
|
||||||
|
<property name="allowEmptyLoops" value="true"/>
|
||||||
|
<property name="allowEmptySwitchBlockStatements" value="true"/>
|
||||||
|
<property name="ignoreEnhancedForColon" value="false"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
|
||||||
|
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
|
||||||
|
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
|
||||||
|
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
|
||||||
|
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
|
||||||
|
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
|
||||||
|
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT,
|
||||||
|
TYPE_EXTENSION_AND, LITERAL_WHEN"/>
|
||||||
|
<message key="ws.notFollowed"
|
||||||
|
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks
|
||||||
|
may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||||
|
<message key="ws.notPreceded"
|
||||||
|
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||||
|
</module>
|
||||||
|
<module name="SuppressionXpathSingleFilter">
|
||||||
|
<property name="checks" value="WhitespaceAround"/>
|
||||||
|
<property name="query" value="//*[self::LITERAL_IF or self::LITERAL_ELSE or
|
||||||
|
self::STATIC_INIT]/SLIST[count(./*)=1]
|
||||||
|
| //*[self::STATIC_INIT or self::LITERAL_TRY or self::LITERAL_IF]
|
||||||
|
//*[self::RCURLY][parent::SLIST[count(./*)=1]]
|
||||||
|
| //SLIST[count(./*)=1][parent::LITERAL_TRY and
|
||||||
|
not(following-sibling::*)]
|
||||||
|
| //SLIST[count(./*)=1][parent::LITERAL_CATCH and
|
||||||
|
not(parent::LITERAL_CATCH/following-sibling::*)]"/>
|
||||||
|
</module>
|
||||||
|
<module name="RegexpSinglelineJava">
|
||||||
|
<property name="format" value="\{[ ]+\}"/>
|
||||||
|
<property name="message" value="Empty blocks should have no spaces. Empty blocks
|
||||||
|
may only be represented as '{}' when not part of a
|
||||||
|
multi-block statement (4.1.3)"/>
|
||||||
|
</module>
|
||||||
|
<module name="OneStatementPerLine"/>
|
||||||
|
<module name="MultipleVariableDeclarations"/>
|
||||||
|
<module name="ArrayTypeStyle"/>
|
||||||
|
<module name="MissingSwitchDefault"/>
|
||||||
|
<module name="FallThrough"/>
|
||||||
|
<module name="UpperEll"/>
|
||||||
|
<module name="ModifierOrder"/>
|
||||||
|
<module name="EmptyLineSeparator">
|
||||||
|
<property name="tokens"
|
||||||
|
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||||
|
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
|
||||||
|
COMPACT_CTOR_DEF"/>
|
||||||
|
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapDot"/>
|
||||||
|
<property name="tokens" value="DOT"/>
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapComma"/>
|
||||||
|
<property name="tokens" value="COMMA"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
|
||||||
|
<property name="id" value="SeparatorWrapEllipsis"/>
|
||||||
|
<property name="tokens" value="ELLIPSIS"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
|
||||||
|
<property name="id" value="SeparatorWrapArrayDeclarator"/>
|
||||||
|
<property name="tokens" value="ARRAY_DECLARATOR"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapMethodRef"/>
|
||||||
|
<property name="tokens" value="METHOD_REF"/>
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
</module>
|
||||||
|
<module name="PackageName">
|
||||||
|
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="TypeName">
|
||||||
|
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||||
|
ANNOTATION_DEF, RECORD_DEF"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="MemberName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="ParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="LambdaParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="CatchParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="LocalVariableName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="PatternVariableName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="ClassTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="RecordComponentName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Record component name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="RecordTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Record type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="InterfaceTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="NoFinalizer"/>
|
||||||
|
<module name="GenericWhitespace">
|
||||||
|
<message key="ws.followed"
|
||||||
|
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||||
|
<message key="ws.preceded"
|
||||||
|
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||||
|
<message key="ws.illegalFollow"
|
||||||
|
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||||
|
<message key="ws.notPreceded"
|
||||||
|
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||||
|
</module>
|
||||||
|
<module name="Indentation">
|
||||||
|
<property name="basicOffset" value="2"/>
|
||||||
|
<property name="braceAdjustment" value="2"/>
|
||||||
|
<property name="caseIndent" value="2"/>
|
||||||
|
<property name="throwsIndent" value="4"/>
|
||||||
|
<property name="lineWrappingIndentation" value="4"/>
|
||||||
|
<property name="arrayInitIndent" value="2"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="AbbreviationAsWordInName">
|
||||||
|
<property name="ignoreFinal" value="false"/>
|
||||||
|
<property name="allowedAbbreviationLength" value="0"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
|
||||||
|
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
|
||||||
|
RECORD_COMPONENT_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="NoWhitespaceBeforeCaseDefaultColon"/>
|
||||||
|
<module name="OverloadMethodsDeclarationOrder"/>
|
||||||
|
<module name="ConstructorsDeclarationGrouping"/>
|
||||||
|
<module name="VariableDeclarationUsageDistance"/>
|
||||||
|
<module name="CustomImportOrder">
|
||||||
|
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||||
|
<property name="separateLineBetweenGroups" value="true"/>
|
||||||
|
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
|
||||||
|
<property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodParamPad">
|
||||||
|
<property name="tokens"
|
||||||
|
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF, CTOR_CALL,
|
||||||
|
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF, RECORD_PATTERN_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="NoWhitespaceBefore">
|
||||||
|
<property name="tokens"
|
||||||
|
value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
|
||||||
|
LABELED_STAT, METHOD_REF"/>
|
||||||
|
<property name="allowLineBreaks" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="ParenPad">
|
||||||
|
<property name="tokens"
|
||||||
|
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
|
||||||
|
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
|
||||||
|
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
|
||||||
|
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
|
||||||
|
RECORD_DEF, RECORD_PATTERN_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="OperatorWrap">
|
||||||
|
<property name="option" value="NL"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||||
|
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
|
||||||
|
TYPE_EXTENSION_AND "/>
|
||||||
|
</module>
|
||||||
|
<module name="AnnotationLocation">
|
||||||
|
<property name="id" value="AnnotationLocationMostCases"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
|
||||||
|
RECORD_DEF, COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="AnnotationLocation">
|
||||||
|
<property name="id" value="AnnotationLocationVariables"/>
|
||||||
|
<property name="tokens" value="VARIABLE_DEF"/>
|
||||||
|
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="NonEmptyAtclauseDescription"/>
|
||||||
|
<module name="InvalidJavadocPosition"/>
|
||||||
|
<module name="JavadocTagContinuationIndentation"/>
|
||||||
|
<module name="SummaryJavadoc">
|
||||||
|
<property name="forbiddenSummaryFragments"
|
||||||
|
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||||
|
</module>
|
||||||
|
<module name="JavadocParagraph">
|
||||||
|
<property name="allowNewlineParagraph" value="false"/>
|
||||||
|
</module>
|
||||||
|
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
|
||||||
|
<module name="AtclauseOrder">
|
||||||
|
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||||
|
<property name="target"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="JavadocMethod">
|
||||||
|
<property name="accessModifiers" value="public"/>
|
||||||
|
<property name="allowMissingParamTags" value="true"/>
|
||||||
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
|
<property name="allowedAnnotations" value="Override, Test"/>
|
||||||
|
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="MissingJavadocMethod">
|
||||||
|
<property name="scope" value="protected"/>
|
||||||
|
<property name="allowMissingPropertyJavadoc" value="true"/>
|
||||||
|
<property name="allowedAnnotations" value="Override, Test"/>
|
||||||
|
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
|
||||||
|
COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="SuppressionXpathSingleFilter">
|
||||||
|
<property name="checks" value="MissingJavadocMethod"/>
|
||||||
|
<property name="query" value="//*[self::METHOD_DEF or self::CTOR_DEF
|
||||||
|
or self::ANNOTATION_FIELD_DEF or self::COMPACT_CTOR_DEF]
|
||||||
|
[ancestor::*[self::INTERFACE_DEF or self::CLASS_DEF
|
||||||
|
or self::RECORD_DEF or self::ENUM_DEF]
|
||||||
|
[not(./MODIFIERS/LITERAL_PUBLIC)]]"/>
|
||||||
|
</module>
|
||||||
|
<module name="MissingJavadocType">
|
||||||
|
<property name="scope" value="protected"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||||
|
RECORD_DEF, ANNOTATION_DEF"/>
|
||||||
|
<property name="excludeScope" value="nothing"/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="SuppressionXpathSingleFilter">
|
||||||
|
<property name="checks" value="MethodName"/>
|
||||||
|
<property name="query" value="//METHOD_DEF[
|
||||||
|
./MODIFIERS/ANNOTATION//IDENT[contains(@text, 'Test')]
|
||||||
|
]/IDENT"/>
|
||||||
|
<property name="message" value="'[a-z][a-z0-9][a-zA-Z0-9]*(?:_[a-z][a-z0-9][a-zA-Z0-9]*)*'"/>
|
||||||
|
</module>
|
||||||
|
<module name="SingleLineJavadoc"/>
|
||||||
|
<module name="EmptyCatchBlock">
|
||||||
|
<property name="exceptionVariableName" value="expected"/>
|
||||||
|
</module>
|
||||||
|
<module name="CommentsIndentation">
|
||||||
|
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
|
||||||
|
</module>
|
||||||
|
<!-- https://checkstyle.org/filters/suppressionxpathfilter.html -->
|
||||||
|
<module name="SuppressionXpathFilter">
|
||||||
|
<property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
|
||||||
|
default="checkstyle-xpath-suppressions.xml" />
|
||||||
|
<property name="optional" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="SuppressWarningsHolder" />
|
||||||
|
<module name="SuppressionCommentFilter">
|
||||||
|
<property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)" />
|
||||||
|
<property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)" />
|
||||||
|
<property name="checkFormat" value="$1" />
|
||||||
|
</module>
|
||||||
|
<module name="SuppressWithNearbyCommentFilter">
|
||||||
|
<property name="commentFormat" value="CHECKSTYLE.SUPPRESS\: ([\w\|]+)"/>
|
||||||
|
<!-- $1 refers to the first match group in the regex defined in commentFormat -->
|
||||||
|
<property name="checkFormat" value="$1"/>
|
||||||
|
<!-- The check is suppressed in the next line of code after the comment -->
|
||||||
|
<property name="influenceFormat" value="1"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
|
@ -12,289 +12,353 @@ import java.util.PriorityQueue;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core class for impl graph.
|
||||||
|
*/
|
||||||
public class Graph {
|
public class Graph {
|
||||||
|
|
||||||
public record EdgeRecord(String node1, String node2, int weight) {
|
private final Map<String, Node> nodes;
|
||||||
|
private Map<Node, Double> pageRanks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial func of Graph.
|
||||||
|
*
|
||||||
|
* @param input input text
|
||||||
|
* @param d d of page rank
|
||||||
|
*/
|
||||||
|
public Graph(final String input, final double d) {
|
||||||
|
nodes = new HashMap<>();
|
||||||
|
parse(input);
|
||||||
|
initRageRanks(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial func of Graph with default d.
|
||||||
|
*
|
||||||
|
* @param input input text
|
||||||
|
*/
|
||||||
|
public Graph(final String input) {
|
||||||
|
nodes = new HashMap<>();
|
||||||
|
parse(input);
|
||||||
|
initRageRanks(0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find bridge words.
|
||||||
|
*
|
||||||
|
* @param word1 first word
|
||||||
|
* @param word2 second word
|
||||||
|
* @return list of bridge words
|
||||||
|
*/
|
||||||
|
public Optional<List<String>> findBridgeWords(final String word1, final String word2) {
|
||||||
|
final Node node1 = nodes.get(word1.toLowerCase(Locale.ENGLISH));
|
||||||
|
final Node node2 = nodes.get(word2.toLowerCase(Locale.ENGLISH));
|
||||||
|
|
||||||
|
if (node1 == null || node2 == null) {
|
||||||
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Node {
|
final var bridgeWords = new ArrayList<String>();
|
||||||
|
for (final var node : node1.edges.keySet()) {
|
||||||
|
if (node.edges.containsKey(node2)) {
|
||||||
|
bridgeWords.add(node.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.of(bridgeWords);
|
||||||
|
}
|
||||||
|
|
||||||
String id;
|
/**
|
||||||
Map<Node, Integer> edges;
|
* Find the shortest path between two words.
|
||||||
|
*
|
||||||
|
* @param word1 first word
|
||||||
|
* @param word2 second word
|
||||||
|
* @return list of the words in the path
|
||||||
|
*/
|
||||||
|
public Optional<List<String>> findShortestPath(final String word1, final String word2) {
|
||||||
|
final var startWord = word1.toLowerCase(Locale.ENGLISH);
|
||||||
|
final var endWord = word2.toLowerCase(Locale.ENGLISH);
|
||||||
|
final var path = new ArrayList<String>();
|
||||||
|
|
||||||
Node(final String id) {
|
if (!nodes.containsKey(startWord) || !nodes.containsKey(endWord)) {
|
||||||
this.id = id;
|
return Optional.empty();
|
||||||
this.edges = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void addEdge(final Node target) {
|
|
||||||
edges.put(target, edges.getOrDefault(target, 0) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getDOTEdges(final Function<EdgeRecord, String> callback) {
|
|
||||||
final var dotEdges = new ArrayList<String>();
|
|
||||||
for (final var entry : edges.entrySet()) {
|
|
||||||
final Node target = entry.getKey();
|
|
||||||
final int weight = entry.getValue();
|
|
||||||
dotEdges.add(
|
|
||||||
callback.apply(new EdgeRecord(id, target.id, weight)) + ";\n");
|
|
||||||
}
|
|
||||||
return dotEdges;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getDOTEdges() {
|
|
||||||
final var dotEdges = new ArrayList<String>();
|
|
||||||
for (final var entry : edges.entrySet()) {
|
|
||||||
final Node target = entry.getKey();
|
|
||||||
final int weight = entry.getValue();
|
|
||||||
dotEdges.add(
|
|
||||||
String.format("%s -> %s [label=\"%d\"];\n", id, target.id, weight));
|
|
||||||
}
|
|
||||||
return dotEdges;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<String, Node> nodes;
|
final Node startNode = nodes.get(startWord);
|
||||||
|
final Node endNode = nodes.get(endWord);
|
||||||
|
|
||||||
private Map<Node, Double> pageRanks;
|
if (startNode == endNode) {
|
||||||
|
path.add(startNode.id);
|
||||||
public Graph(final String input, final double d) {
|
return Optional.of(path);
|
||||||
nodes = new HashMap<>();
|
|
||||||
parse(input);
|
|
||||||
initRageRanks(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Graph(final String input) {
|
final var distances = new HashMap<Node, Integer>();
|
||||||
nodes = new HashMap<>();
|
final var previousNodes = new HashMap<Node, Node>();
|
||||||
parse(input);
|
final var queue = new PriorityQueue<Node>(
|
||||||
initRageRanks(0.85);
|
Comparator.comparingInt(node -> distances.getOrDefault(node, Integer.MAX_VALUE)));
|
||||||
|
|
||||||
|
for (final var node : nodes.values()) {
|
||||||
|
distances.put(node, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
distances.put(startNode, 0);
|
||||||
|
queue.add(startNode);
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
final Node current = queue.poll();
|
||||||
|
for (final var edge : current.edges.entrySet()) {
|
||||||
|
final Node neighbor = edge.getKey();
|
||||||
|
final int weight = edge.getValue();
|
||||||
|
|
||||||
|
final int newDist = distances.get(current) + weight;
|
||||||
|
if (newDist < distances.get(neighbor)) {
|
||||||
|
distances.put(neighbor, newDist);
|
||||||
|
previousNodes.put(neighbor, current);
|
||||||
|
queue.add(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<List<String>> findBridgeWords(final String word1, final String word2) {
|
if (distances.get(endNode) == Integer.MAX_VALUE) {
|
||||||
final Node node1 = nodes.get(word1.toLowerCase(Locale.ENGLISH));
|
return Optional.of(path);
|
||||||
final Node node2 = nodes.get(word2.toLowerCase(Locale.ENGLISH));
|
|
||||||
|
|
||||||
if (node1 == null || node2 == null) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
final var bridgeWords = new ArrayList<String>();
|
|
||||||
for (final var node : node1.edges.keySet()) {
|
|
||||||
if (node.edges.containsKey(node2)) {
|
|
||||||
bridgeWords.add(node.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Optional.of(bridgeWords);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<List<String>> findShortestPath(final String word1, final String word2) {
|
final var reversePath = new LinkedList<String>();
|
||||||
final var startWord = word1.toLowerCase(Locale.ENGLISH);
|
Node current = endNode;
|
||||||
final var endWord = word2.toLowerCase(Locale.ENGLISH);
|
while (current != null) {
|
||||||
final var path = new ArrayList<String>();
|
reversePath.addFirst(current.id);
|
||||||
|
current = previousNodes.get(current);
|
||||||
if (!nodes.containsKey(startWord) || !nodes.containsKey(endWord)) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
final Node startNode = nodes.get(startWord);
|
|
||||||
final Node endNode = nodes.get(endWord);
|
|
||||||
|
|
||||||
if (startNode == endNode) {
|
|
||||||
path.add(startNode.id);
|
|
||||||
return Optional.of(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var distances = new HashMap<Node, Integer>();
|
|
||||||
final var previousNodes = new HashMap<Node, Node>();
|
|
||||||
final var queue = new PriorityQueue<Node>(
|
|
||||||
Comparator.comparingInt(node -> distances.getOrDefault(node, Integer.MAX_VALUE)));
|
|
||||||
|
|
||||||
for (final var node : nodes.values()) {
|
|
||||||
distances.put(node, Integer.MAX_VALUE);
|
|
||||||
}
|
|
||||||
distances.put(startNode, 0);
|
|
||||||
queue.add(startNode);
|
|
||||||
|
|
||||||
while (!queue.isEmpty()) {
|
|
||||||
final Node current = queue.poll();
|
|
||||||
for (final var edge : current.edges.entrySet()) {
|
|
||||||
final Node neighbor = edge.getKey();
|
|
||||||
final int weight = edge.getValue();
|
|
||||||
|
|
||||||
final int newDist = distances.get(current) + weight;
|
|
||||||
if (newDist < distances.get(neighbor)) {
|
|
||||||
distances.put(neighbor, newDist);
|
|
||||||
previousNodes.put(neighbor, current);
|
|
||||||
queue.add(neighbor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distances.get(endNode) == Integer.MAX_VALUE) {
|
|
||||||
return Optional.of(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var reversePath = new LinkedList<String>();
|
|
||||||
Node current = endNode;
|
|
||||||
while (current != null) {
|
|
||||||
reversePath.addFirst(current.id);
|
|
||||||
current = previousNodes.get(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.of(reversePath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double computePageRank(final String nodeId) {
|
return Optional.of(reversePath);
|
||||||
final Node node = nodes.get(nodeId);
|
}
|
||||||
if (node == null) {
|
|
||||||
return -1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pageRanks.getOrDefault(node, -1.0);
|
/**
|
||||||
|
* Calculate rage rank.
|
||||||
|
*
|
||||||
|
* @param nodeId id of node
|
||||||
|
* @return page rank
|
||||||
|
*/
|
||||||
|
public double computePageRank(final String nodeId) {
|
||||||
|
final Node node = nodes.get(nodeId);
|
||||||
|
if (node == null) {
|
||||||
|
return -1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> randomWalk() {
|
return pageRanks.getOrDefault(node, -1.0);
|
||||||
final var path = new ArrayList<String>();
|
}
|
||||||
if (nodes.isEmpty()) {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
final var random = new Random();
|
/**
|
||||||
final var nodeIds = new ArrayList<>(nodes.keySet());
|
* Random walk.
|
||||||
final String startNodeId = nodeIds.get(random.nextInt(nodeIds.size()));
|
*
|
||||||
path.add(startNodeId);
|
* @return list of words in the walk path
|
||||||
|
*/
|
||||||
final var usedEdges = new HashMap<String, Integer>();
|
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("PREDICTABLE_RANDOM")
|
||||||
var currentNode = nodes.get(startNodeId);
|
public List<String> randomWalk() {
|
||||||
while (true) {
|
final var path = new ArrayList<String>();
|
||||||
final var availableEdge = new ArrayList<Map.Entry<Node, Integer>>();
|
if (nodes.isEmpty()) {
|
||||||
int totalWeight = 0;
|
return path;
|
||||||
for (final var entry : currentNode.edges.entrySet()) {
|
|
||||||
final String edgeKey = currentNode.id + " -> " + entry.getKey().id;
|
|
||||||
if (usedEdges.getOrDefault(edgeKey, 0) < 2) {
|
|
||||||
availableEdge.add(entry);
|
|
||||||
totalWeight += entry.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (availableEdge.isEmpty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
final double randomValue = random.nextDouble() * totalWeight;
|
|
||||||
double cumulative = 0.0;
|
|
||||||
Node nextNode = null;
|
|
||||||
|
|
||||||
for (final var entry : availableEdge) {
|
|
||||||
cumulative += entry.getValue();
|
|
||||||
if (randomValue < cumulative) {
|
|
||||||
nextNode = entry.getKey();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert nextNode != null;
|
|
||||||
final String edgeKey = currentNode.id + " -> " + nextNode.id;
|
|
||||||
final int edgeTimes = usedEdges.getOrDefault(edgeKey, 0) + 1;
|
|
||||||
usedEdges.put(edgeKey, edgeTimes);
|
|
||||||
path.add(nextNode.id);
|
|
||||||
currentNode = nextNode;
|
|
||||||
|
|
||||||
if (edgeTimes >= 2) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toDOT(final Function<EdgeRecord, String> callback) {
|
final var random = new Random();
|
||||||
final var sb = new StringBuilder();
|
final var nodeIds = new ArrayList<>(nodes.keySet());
|
||||||
sb.append("digraph G {\n");
|
final String startNodeId = nodeIds.get(random.nextInt(nodeIds.size()));
|
||||||
for (final Node node : nodes.values()) {
|
path.add(startNodeId);
|
||||||
for (final String edge : node.getDOTEdges(callback)) {
|
|
||||||
sb.append(" ").append(edge);
|
final var usedEdges = new HashMap<String, Integer>();
|
||||||
}
|
var currentNode = nodes.get(startNodeId);
|
||||||
|
while (true) {
|
||||||
|
final var availableEdge = new ArrayList<Map.Entry<Node, Integer>>();
|
||||||
|
int totalWeight = 0;
|
||||||
|
for (final var entry : currentNode.edges.entrySet()) {
|
||||||
|
final String edgeKey = currentNode.id + " -> " + entry.getKey().id;
|
||||||
|
if (usedEdges.getOrDefault(edgeKey, 0) < 2) {
|
||||||
|
availableEdge.add(entry);
|
||||||
|
totalWeight += entry.getValue();
|
||||||
}
|
}
|
||||||
sb.append("}");
|
}
|
||||||
return sb.toString();
|
|
||||||
|
if (availableEdge.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final double randomValue = random.nextDouble() * totalWeight;
|
||||||
|
double cumulative = 0.0;
|
||||||
|
Node nextNode = null;
|
||||||
|
|
||||||
|
for (final var entry : availableEdge) {
|
||||||
|
cumulative += entry.getValue();
|
||||||
|
if (randomValue < cumulative) {
|
||||||
|
nextNode = entry.getKey();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert nextNode != null;
|
||||||
|
final String edgeKey = currentNode.id + " -> " + nextNode.id;
|
||||||
|
final int edgeTimes = usedEdges.getOrDefault(edgeKey, 0) + 1;
|
||||||
|
usedEdges.put(edgeKey, edgeTimes);
|
||||||
|
path.add(nextNode.id);
|
||||||
|
currentNode = nextNode;
|
||||||
|
|
||||||
|
if (edgeTimes >= 2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate DOT lang to describe the graph.
|
||||||
|
*
|
||||||
|
* @param callback function to modify the option of the node
|
||||||
|
* @return string of DOT lang
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
|
||||||
|
public String toDOT(final Function<EdgeRecord, String> callback) {
|
||||||
|
final var sb = new StringBuilder();
|
||||||
|
sb.append("digraph G {\n");
|
||||||
|
for (final Node node : nodes.values()) {
|
||||||
|
for (final String edge : node.getDOTEdges(callback)) {
|
||||||
|
sb.append(" ").append(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate DOT lang to describe the graph without callback lambda function.
|
||||||
|
*
|
||||||
|
* @return string of DOT lang
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
|
||||||
|
public String toDOT() {
|
||||||
|
final var sb = new StringBuilder();
|
||||||
|
sb.append("digraph G {\n");
|
||||||
|
for (final Node node : nodes.values()) {
|
||||||
|
for (final String edge : node.getDOTEdges()) {
|
||||||
|
sb.append(" ").append(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parse(final String input) {
|
||||||
|
final String[] tokens = input.toLowerCase(Locale.ENGLISH).split("[ \n\r]+");
|
||||||
|
final var words = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (final var token : tokens) {
|
||||||
|
final String word = token.replaceAll("[^a-zA-Z]", "");
|
||||||
|
if (!word.isEmpty()) {
|
||||||
|
words.add(word);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toDOT() {
|
for (int i = 0; i < words.size() - 1; i++) {
|
||||||
final var sb = new StringBuilder();
|
final String currentWord = words.get(i);
|
||||||
sb.append("digraph G {\n");
|
final String nextWord = words.get(i + 1);
|
||||||
for (final Node node : nodes.values()) {
|
|
||||||
for (final String edge : node.getDOTEdges()) {
|
final Node currentNode = getOrCreateNode(currentWord);
|
||||||
sb.append(" ").append(edge);
|
final Node nextNode = getOrCreateNode(nextWord);
|
||||||
}
|
|
||||||
}
|
currentNode.addEdge(nextNode);
|
||||||
sb.append("}");
|
}
|
||||||
return sb.toString();
|
}
|
||||||
|
|
||||||
|
private Node getOrCreateNode(final String word) {
|
||||||
|
return nodes.computeIfAbsent(word, Node::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initRageRanks(final double d) {
|
||||||
|
final int totalNodes = nodes.size();
|
||||||
|
if (totalNodes == 0) {
|
||||||
|
pageRanks = new HashMap<>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var myPageRanks = new HashMap<Node, Double>();
|
||||||
|
final double initRank = 1.0 / totalNodes;
|
||||||
|
for (final var n : nodes.values()) {
|
||||||
|
myPageRanks.put(n, initRank);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parse(final String input) {
|
final int iterations = 100;
|
||||||
final String[] tokens = input.toLowerCase(Locale.ENGLISH).split("[ \n\r]+");
|
for (int i = 0; i < iterations; i++) {
|
||||||
final var words = new ArrayList<String>();
|
final var newPageRanks = new HashMap<Node, Double>();
|
||||||
|
nodes.values().forEach(n -> newPageRanks.put(n, 0.0));
|
||||||
|
for (final var curr : nodes.values()) {
|
||||||
|
final int totalEdges = curr.edges.size();
|
||||||
|
|
||||||
for (final var token : tokens) {
|
for (final var entry : curr.edges.entrySet()) {
|
||||||
final String word = token.replaceAll("[^a-zA-Z]", "");
|
final Node neighbor = entry.getKey();
|
||||||
if (!word.isEmpty()) {
|
final double contribution = myPageRanks.get(curr) / totalEdges;
|
||||||
words.add(word);
|
newPageRanks.put(neighbor, newPageRanks.get(neighbor) + contribution);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < words.size() - 1; i++) {
|
final double damplingTerm = (1.0 - d) / totalNodes;
|
||||||
final String currentWord = words.get(i);
|
nodes.values().forEach(n -> newPageRanks.put(n, damplingTerm + d * newPageRanks.get(n)));
|
||||||
final String nextWord = words.get(i + 1);
|
myPageRanks = newPageRanks;
|
||||||
|
|
||||||
final Node currentNode = getOrCreateNode(currentWord);
|
|
||||||
final Node nextNode = getOrCreateNode(nextWord);
|
|
||||||
|
|
||||||
currentNode.addEdge(nextNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node getOrCreateNode(final String word) {
|
pageRanks = myPageRanks;
|
||||||
return nodes.computeIfAbsent(word, Node::new);
|
for (final var node : nodes.values()) {
|
||||||
|
if (node.edges.isEmpty()) {
|
||||||
|
nodes.values().stream()
|
||||||
|
.filter(n -> !n.equals(node))
|
||||||
|
.forEach(n -> pageRanks
|
||||||
|
.put(n, pageRanks.get(n) + pageRanks.get(node) / (totalNodes - 1)));
|
||||||
|
pageRanks.put(node, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record of edge.
|
||||||
|
*
|
||||||
|
* @param node1 fist node
|
||||||
|
* @param node2 second node
|
||||||
|
* @param weight weight of edge
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public record EdgeRecord(String node1, String node2, int weight) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Node {
|
||||||
|
|
||||||
|
String id;
|
||||||
|
Map<Node, Integer> edges;
|
||||||
|
|
||||||
|
Node(final String id) {
|
||||||
|
this.id = id;
|
||||||
|
this.edges = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initRageRanks(final double d) {
|
void addEdge(final Node target) {
|
||||||
final int totalNodes = nodes.size();
|
edges.put(target, edges.getOrDefault(target, 0) + 1);
|
||||||
if (totalNodes == 0) {
|
|
||||||
pageRanks = new HashMap<>();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var myPageRanks = new HashMap<Node, Double>();
|
|
||||||
final double initRank = 1.0 / totalNodes;
|
|
||||||
for (final var n : nodes.values()) {
|
|
||||||
myPageRanks.put(n, initRank);
|
|
||||||
}
|
|
||||||
|
|
||||||
final int iterations = 100;
|
|
||||||
for (int i = 0; i < iterations; i++) {
|
|
||||||
final var newPageRanks = new HashMap<Node, Double>();
|
|
||||||
nodes.values().forEach(n -> newPageRanks.put(n, 0.0));
|
|
||||||
for (final var curr : nodes.values()) {
|
|
||||||
final int totalEdges = curr.edges.size();
|
|
||||||
|
|
||||||
for (final var entry : curr.edges.entrySet()) {
|
|
||||||
final Node neighbor = entry.getKey();
|
|
||||||
final double contribution = myPageRanks.get(curr) / totalEdges;
|
|
||||||
newPageRanks.put(neighbor, newPageRanks.get(neighbor) + contribution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final double damplingTerm = (1.0 - d) / totalNodes;
|
|
||||||
nodes.values().forEach(n -> newPageRanks.put(n, damplingTerm + d * newPageRanks.get(n)));
|
|
||||||
myPageRanks = newPageRanks;
|
|
||||||
}
|
|
||||||
|
|
||||||
pageRanks = myPageRanks;
|
|
||||||
for (final var node : nodes.values()) {
|
|
||||||
if (node.edges.isEmpty()) {
|
|
||||||
nodes.values().stream().filter(n -> !n.equals(node))
|
|
||||||
.forEach(n -> pageRanks.put(n, pageRanks.get(n) + pageRanks.get(node) / (totalNodes - 1)));
|
|
||||||
pageRanks.put(node, 0.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
|
||||||
|
List<String> getDOTEdges(final Function<EdgeRecord, String> callback) {
|
||||||
|
final var dotEdges = new ArrayList<String>();
|
||||||
|
for (final var entry : edges.entrySet()) {
|
||||||
|
final Node target = entry.getKey();
|
||||||
|
final int weight = entry.getValue();
|
||||||
|
dotEdges.add(
|
||||||
|
callback.apply(new EdgeRecord(id, target.id, weight)) + ";\n");
|
||||||
|
}
|
||||||
|
return dotEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
|
||||||
|
List<String> getDOTEdges() {
|
||||||
|
final var dotEdges = new ArrayList<String>();
|
||||||
|
for (final var entry : edges.entrySet()) {
|
||||||
|
final Node target = entry.getKey();
|
||||||
|
final int weight = entry.getValue();
|
||||||
|
dotEdges.add(
|
||||||
|
String.format("%s -> %s [label=\"%d\"];%n", id, target.id, weight));
|
||||||
|
}
|
||||||
|
return dotEdges;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package fun.youthlic;
|
package fun.youthlic;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
|
@ -15,324 +15,333 @@ import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.jline.reader.LineReader;
|
import org.jline.reader.LineReader;
|
||||||
import org.jline.reader.LineReaderBuilder;
|
import org.jline.reader.LineReaderBuilder;
|
||||||
import org.jline.reader.impl.DefaultParser;
|
import org.jline.reader.impl.DefaultParser;
|
||||||
import org.jline.terminal.Terminal;
|
import org.jline.terminal.Terminal;
|
||||||
import org.jline.terminal.TerminalBuilder;
|
import org.jline.terminal.TerminalBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for running in Commandline.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
public class GraphCLI {
|
public class GraphCLI {
|
||||||
static String readFile(final String filename) {
|
@SuppressFBWarnings({"DM_DEFAULT_ENCODING", "PATH_TRAVERSAL_IN"})
|
||||||
String text = null;
|
static String readFile(final String filename) {
|
||||||
try (BufferedReader inputReader = new BufferedReader(new FileReader(new File(filename)))) {
|
String text = null;
|
||||||
text = inputReader.lines().collect(Collectors.joining(System.lineSeparator()));
|
try (BufferedReader inputReader = new BufferedReader(new FileReader(filename))) {
|
||||||
} catch (final IOException e) {
|
text = inputReader.lines().collect(Collectors.joining(System.lineSeparator()));
|
||||||
System.out.println("Error reading file: " + e.getMessage());
|
} catch (final IOException e) {
|
||||||
}
|
System.out.println("Error reading file: " + e.getMessage());
|
||||||
return text;
|
|
||||||
}
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(final String[] args) {
|
/**
|
||||||
GraphCLIHelper helper = null;
|
* CommandLine entry point.
|
||||||
Terminal terminal = null;
|
*
|
||||||
try {
|
* @param args launch args
|
||||||
terminal = TerminalBuilder.builder().system(true).build();
|
*/
|
||||||
} catch (final IOException e) {
|
public static void main(final String[] args) {
|
||||||
e.printStackTrace();
|
GraphCLIHelper helper = null;
|
||||||
System.exit(-1);
|
Terminal terminal = null;
|
||||||
}
|
try {
|
||||||
final LineReader reader = LineReaderBuilder.builder().terminal(terminal).parser(new DefaultParser())
|
terminal = TerminalBuilder.builder().system(true).build();
|
||||||
.build();
|
} catch (final IOException e) {
|
||||||
while (true) {
|
e.printStackTrace();
|
||||||
final String line = reader.readLine("graph> ");
|
System.exit(-1);
|
||||||
final var commands = line.split("[ \\t]+");
|
|
||||||
if (commands.length == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (commands[0]) {
|
|
||||||
case "load":
|
|
||||||
if (commands.length != 2) {
|
|
||||||
System.out.println("Usage: load <filename>");
|
|
||||||
} else {
|
|
||||||
final var text = readFile(commands[1]);
|
|
||||||
if (text == null) {
|
|
||||||
System.out.println("Load file failed; " + commands[1]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
helper = new GraphCLIHelper(text);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "help":
|
|
||||||
System.out.println("Available commands:");
|
|
||||||
System.out.println(" show");
|
|
||||||
System.out.println(" dot");
|
|
||||||
System.out.println(" help");
|
|
||||||
System.out.println(" exit");
|
|
||||||
System.out.println(" random-walk");
|
|
||||||
System.out.println(" load <filename>");
|
|
||||||
System.out.println(" page-rank <word>");
|
|
||||||
System.out.println(" bridge-words <word1> <word2>");
|
|
||||||
System.out.println(" new-text <text...>");
|
|
||||||
System.out.println(" shortest-path <word1> <word2>");
|
|
||||||
break;
|
|
||||||
case "shortest-path":
|
|
||||||
if (commands.length != 3) {
|
|
||||||
System.out.println("Usage: shortest-path <word1> <word2>");
|
|
||||||
} else if (helper == null) {
|
|
||||||
System.out.println("No graph loaded");
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
System.out.println(helper.calcShortestPath(commands[1], commands[2]));
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
System.out.println(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "exit":
|
|
||||||
return;
|
|
||||||
case "random-walk":
|
|
||||||
if (commands.length != 1) {
|
|
||||||
System.out.println("Usage: random-walk");
|
|
||||||
} else {
|
|
||||||
if (helper == null) {
|
|
||||||
System.out.println("No graph loaded");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
System.out.println(helper.randomWalk());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "new-text":
|
|
||||||
if (commands.length < 2) {
|
|
||||||
System.out.println("Usage: new-text <text...>");
|
|
||||||
} else if (helper == null) {
|
|
||||||
System.out.println("No graph loaded");
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
System.out.println(helper.generateNewText(
|
|
||||||
String.join(" ", Arrays.asList(commands).subList(1, commands.length))));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "show":
|
|
||||||
if (commands.length != 1) {
|
|
||||||
System.out.println("Usage: show");
|
|
||||||
} else {
|
|
||||||
if (helper == null) {
|
|
||||||
System.out.println("No graph loaded");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
GraphCLIHelper.showDirectedGraph(helper.graph);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "dot":
|
|
||||||
if (commands.length != 1) {
|
|
||||||
System.out.println("Usage: dot");
|
|
||||||
} else {
|
|
||||||
if (helper == null) {
|
|
||||||
System.out.println("No graph loaded");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
System.out.println(helper.graph.toDOT());
|
|
||||||
}
|
|
||||||
case "page-rank":
|
|
||||||
if (commands.length != 2) {
|
|
||||||
System.out.println("Usage: page-rank <word>");
|
|
||||||
} else if (helper == null) {
|
|
||||||
System.out.println("No graph loaded");
|
|
||||||
} else {
|
|
||||||
final var pageRank = helper.calPageRank(commands[1]);
|
|
||||||
if (pageRank < 0.0) {
|
|
||||||
System.out.println("Invalid word");
|
|
||||||
} else {
|
|
||||||
System.out.println("PageRank of " + commands[1] + ": " + pageRank);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "bridge-words":
|
|
||||||
if (commands.length != 3) {
|
|
||||||
System.out.println("Usage: bridge-words <word1> <word2>");
|
|
||||||
} else if (helper == null) {
|
|
||||||
System.out.println("No graph loaded");
|
|
||||||
} else {
|
|
||||||
System.out.println(helper.queryBridgeWords(commands[1], commands[2]));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
System.out.println("Unknown command: " + commands[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
final LineReader reader = LineReaderBuilder.builder().terminal(terminal)
|
||||||
|
.parser(new DefaultParser())
|
||||||
|
.build();
|
||||||
|
while (true) {
|
||||||
|
final String line = reader.readLine("graph> ");
|
||||||
|
final var commands = line.split("[ \\t]+");
|
||||||
|
if (commands.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (commands[0]) {
|
||||||
|
case "load":
|
||||||
|
if (commands.length != 2) {
|
||||||
|
System.out.println("Usage: load <filename>");
|
||||||
|
} else {
|
||||||
|
final var text = readFile(commands[1]);
|
||||||
|
if (text == null) {
|
||||||
|
System.out.println("Load file failed; " + commands[1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
helper = new GraphCLIHelper(text);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "help":
|
||||||
|
System.out.println("Available commands:");
|
||||||
|
System.out.println(" show");
|
||||||
|
System.out.println(" dot");
|
||||||
|
System.out.println(" help");
|
||||||
|
System.out.println(" exit");
|
||||||
|
System.out.println(" random-walk");
|
||||||
|
System.out.println(" load <filename>");
|
||||||
|
System.out.println(" page-rank <word>");
|
||||||
|
System.out.println(" bridge-words <word1> <word2>");
|
||||||
|
System.out.println(" new-text <text...>");
|
||||||
|
System.out.println(" shortest-path <word1> <word2>");
|
||||||
|
break;
|
||||||
|
case "shortest-path":
|
||||||
|
if (commands.length != 3) {
|
||||||
|
System.out.println("Usage: shortest-path <word1> <word2>");
|
||||||
|
} else if (helper == null) {
|
||||||
|
System.out.println("No graph loaded");
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
System.out.println(helper.calcShortestPath(commands[1], commands[2]));
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "exit":
|
||||||
|
return;
|
||||||
|
case "random-walk":
|
||||||
|
if (commands.length != 1) {
|
||||||
|
System.out.println("Usage: random-walk");
|
||||||
|
} else {
|
||||||
|
if (helper == null) {
|
||||||
|
System.out.println("No graph loaded");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
System.out.println(helper.randomWalk());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "new-text":
|
||||||
|
if (commands.length < 2) {
|
||||||
|
System.out.println("Usage: new-text <text...>");
|
||||||
|
} else if (helper == null) {
|
||||||
|
System.out.println("No graph loaded");
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
System.out.println(helper.generateNewText(
|
||||||
|
String.join(" ", Arrays.asList(commands).subList(1, commands.length))));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "show":
|
||||||
|
if (commands.length != 1) {
|
||||||
|
System.out.println("Usage: show");
|
||||||
|
} else {
|
||||||
|
if (helper == null) {
|
||||||
|
System.out.println("No graph loaded");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
GraphCLIHelper.showDirectedGraph(helper.graph);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "dot":
|
||||||
|
if (commands.length != 1) {
|
||||||
|
System.out.println("Usage: dot");
|
||||||
|
} else {
|
||||||
|
if (helper == null) {
|
||||||
|
System.out.println("No graph loaded");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
System.out.println(helper.graph.toDOT());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "page-rank":
|
||||||
|
if (commands.length != 2) {
|
||||||
|
System.out.println("Usage: page-rank <word>");
|
||||||
|
} else if (helper == null) {
|
||||||
|
System.out.println("No graph loaded");
|
||||||
|
} else {
|
||||||
|
final var pageRank = helper.calPageRank(commands[1]);
|
||||||
|
if (pageRank < 0.0) {
|
||||||
|
System.out.println("Invalid word");
|
||||||
|
} else {
|
||||||
|
System.out.println("PageRank of " + commands[1] + ": " + pageRank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "bridge-words":
|
||||||
|
if (commands.length != 3) {
|
||||||
|
System.out.println("Usage: bridge-words <word1> <word2>");
|
||||||
|
} else if (helper == null) {
|
||||||
|
System.out.println("No graph loaded");
|
||||||
|
} else {
|
||||||
|
System.out.println(helper.queryBridgeWords(commands[1], commands[2]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("Unknown command: " + commands[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for CommandLine.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("ALL")
|
||||||
class GraphCLIHelper {
|
class GraphCLIHelper {
|
||||||
Graph graph;
|
Graph graph;
|
||||||
|
|
||||||
GraphCLIHelper(final String text) {
|
GraphCLIHelper(final String text) {
|
||||||
graph = new Graph(text);
|
graph = new Graph(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showDirectedGraph(final Graph graph) {
|
||||||
|
final ProcessBuilder processBuilder = new ProcessBuilder("graph-easy", "--as", "ascii");
|
||||||
|
processBuilder.redirectErrorStream(true);
|
||||||
|
Process process;
|
||||||
|
try {
|
||||||
|
process = processBuilder.start();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
System.err.println("Failed to start process");
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(
|
||||||
static void showDirectedGraph(final Graph G) {
|
new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8))) {
|
||||||
final ProcessBuilder processBuilder = new ProcessBuilder("graph-easy", "--as", "ascii");
|
writer.write(graph.toDOT());
|
||||||
processBuilder.redirectErrorStream(true);
|
writer.flush();
|
||||||
Process process = null;
|
} catch (final IOException e) {
|
||||||
try {
|
System.err.println("Failed to write to process");
|
||||||
process = processBuilder.start();
|
e.printStackTrace();
|
||||||
} catch (final IOException e) {
|
|
||||||
System.err.println("Failed to start process");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try (BufferedWriter writer = new BufferedWriter(
|
|
||||||
new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8))) {
|
|
||||||
writer.write(G.toDOT());
|
|
||||||
writer.flush();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
System.err.println("Failed to write to process");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
final StringBuilder output = new StringBuilder();
|
|
||||||
try (BufferedReader reader = new BufferedReader(
|
|
||||||
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
output.append(line).append(System.lineSeparator());
|
|
||||||
}
|
|
||||||
} catch (final IOException e) {
|
|
||||||
System.err.println("Failed to read from process");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final int exitCode = process.waitFor();
|
|
||||||
if (exitCode != 0) {
|
|
||||||
System.err.println("Process exited with code " + exitCode);
|
|
||||||
} else {
|
|
||||||
System.out.println(output.toString());
|
|
||||||
}
|
|
||||||
} catch (final InterruptedException e) {
|
|
||||||
System.err.println("Process interrupted");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
final StringBuilder output = new StringBuilder();
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.append(line).append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
System.err.println("Failed to read from process");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final int exitCode = process.waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
System.err.println("Process exited with code " + exitCode);
|
||||||
|
} else {
|
||||||
|
System.out.println(output.toString());
|
||||||
|
}
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
System.err.println("Process interrupted");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String queryBridgeWords(final String word1, final String word2) {
|
String queryBridgeWords(final String word1, final String word2) {
|
||||||
final var option = graph.findBridgeWords(word1, word2);
|
final var option = graph.findBridgeWords(word1, word2);
|
||||||
if (option.isEmpty()) {
|
if (option.isEmpty()) {
|
||||||
return "No " + word1 + " or " + word2 + " in the graph!";
|
return "No " + word1 + " or " + word2 + " in the graph!";
|
||||||
}
|
}
|
||||||
final var bridgeWords = option.get();
|
final var bridgeWords = option.get();
|
||||||
if (bridgeWords.isEmpty()) {
|
if (bridgeWords.isEmpty()) {
|
||||||
return "No bridge words from " + word1 + " to " + word2 + "!";
|
return "No bridge words from " + word1 + " to " + word2 + "!";
|
||||||
}
|
}
|
||||||
final var output = new StringBuilder();
|
final var output = new StringBuilder();
|
||||||
output.append("The bridge words from ").append(word1).append(" to ").append(word2).append(" are: ");
|
output.append("The bridge words from ").append(word1).append(" to ").append(word2).append(" are: ");
|
||||||
for (int i = 0; i < bridgeWords.size(); i++) {
|
for (int i = 0; i < bridgeWords.size(); i++) {
|
||||||
output.append(bridgeWords.get(i));
|
output.append(bridgeWords.get(i));
|
||||||
if (i < bridgeWords.size() - 2) {
|
if (i < bridgeWords.size() - 2) {
|
||||||
output.append(", ");
|
output.append(", ");
|
||||||
} else if (i == bridgeWords.size() - 2) {
|
} else if (i == bridgeWords.size() - 2) {
|
||||||
output.append(", and ");
|
output.append(", and ");
|
||||||
} else {
|
} else {
|
||||||
output.append(".");
|
output.append(".");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings({"PREDICTABLE_RANDOM", "DMI_RANDOM_USED_ONLY_ONCE"})
|
||||||
|
String generateNewText(final String inputText) {
|
||||||
|
final var output = new StringBuilder();
|
||||||
|
final var words = inputText.split("\\s+");
|
||||||
|
for (int i = 0; i < words.length; i++) {
|
||||||
|
output.append(words[i]);
|
||||||
|
if (i < words.length - 1) {
|
||||||
|
output.append(" ");
|
||||||
|
final var option = graph.findBridgeWords(words[i], words[i + 1]);
|
||||||
|
if (option.isPresent() && !option.get().isEmpty()) {
|
||||||
|
final var bridgeWords = option.get();
|
||||||
|
final Random random = new Random();
|
||||||
|
final int wordIndex = random.nextInt(bridgeWords.size());
|
||||||
|
output.append(option.get().get(wordIndex)).append(" ");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String calcShortestPath(final String word1, final String word2)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
final ProcessBuilder processBuilder = new ProcessBuilder("graph-easy", "--as", "ascii");
|
||||||
|
processBuilder.redirectErrorStream(true);
|
||||||
|
Process process = null;
|
||||||
|
process = processBuilder.start();
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(
|
||||||
|
new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
writer.write(calcShortestPathToDOT(word1, word2));
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
final StringBuilder output = new StringBuilder();
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.append(line).append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final int exitCode = process.waitFor();
|
||||||
|
if (exitCode != 0) {
|
||||||
|
throw new IOException("Process exited with code " + exitCode);
|
||||||
|
} else {
|
||||||
return output.toString();
|
return output.toString();
|
||||||
|
}
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String generateNewText(final String inputText) {
|
String calcShortestPathToDOT(final String word1, final String word2) {
|
||||||
final var output = new StringBuilder();
|
var option = graph.findShortestPath(word1, word2);
|
||||||
final var words = inputText.split("\\s+");
|
if (option.isEmpty()) {
|
||||||
for (int i = 0; i < words.length; i++) {
|
return "No " + word1 + " or " + word2 + " in the graph";
|
||||||
output.append(words[i]);
|
} else {
|
||||||
if (i < words.length - 1) {
|
final var path = option.get();
|
||||||
output.append(" ");
|
if (path.isEmpty()) {
|
||||||
final var option = graph.findBridgeWords(words[i], words[i + 1]);
|
return "No path from " + word1 + " to " + word2;
|
||||||
if (option.isPresent() && !option.get().isEmpty()) {
|
}
|
||||||
final var bridgeWords = option.get();
|
return graph.toDOT(r -> String.format(
|
||||||
final Random random = new Random();
|
"\"%s\" -> \"%s\" [label=\"%d\"%s]",
|
||||||
final int wordIndex = random.nextInt(bridgeWords.size());
|
r.node1() + (path.contains(r.node1()) ? "~" : ""),
|
||||||
output.append(option.get().get(wordIndex)).append(" ");
|
r.node2() + (path.contains(r.node2()) ? "~" : ""),
|
||||||
}
|
r.weight(),
|
||||||
}
|
(path.contains(r.node1()) && path.contains(r.node2())
|
||||||
}
|
&& IntStream.range(0, path.size() - 1).anyMatch(
|
||||||
return output.toString();
|
i -> path.get(i).equals(r.node1()) && path.get(i + 1).equals(r.node2())))
|
||||||
|
? ",color=\"red\""
|
||||||
|
: ""));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String calcShortestPath(final String word1, final String word2)
|
Double calPageRank(final String word) {
|
||||||
throws IOException, InterruptedException {
|
return graph.computePageRank(word);
|
||||||
final ProcessBuilder processBuilder = new ProcessBuilder("graph-easy", "--as", "ascii");
|
}
|
||||||
processBuilder.redirectErrorStream(true);
|
|
||||||
Process process = null;
|
|
||||||
try {
|
|
||||||
process = processBuilder.start();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
try (BufferedWriter writer = new BufferedWriter(
|
|
||||||
new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8))) {
|
|
||||||
writer.write(calcShortestPathToDOT(word1, word2));
|
|
||||||
writer.flush();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
final StringBuilder output = new StringBuilder();
|
|
||||||
try (BufferedReader reader = new BufferedReader(
|
|
||||||
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
output.append(line).append(System.lineSeparator());
|
|
||||||
}
|
|
||||||
} catch (final IOException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final int exitCode = process.waitFor();
|
|
||||||
if (exitCode != 0) {
|
|
||||||
throw new IOException("Process exited with code " + exitCode);
|
|
||||||
} else {
|
|
||||||
return output.toString();
|
|
||||||
}
|
|
||||||
} catch (final InterruptedException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String calcShortestPathToDOT(final String word1, final String word2) {
|
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
|
||||||
var option = graph.findShortestPath(word1, word2);
|
String randomWalk() {
|
||||||
if (option.isEmpty()) {
|
final var text = String.join(" ", graph.randomWalk());
|
||||||
return "No " + word1 + " or " + word2 + " in the graph";
|
final Path path = Paths.get("output.txt");
|
||||||
} else {
|
try {
|
||||||
final var path = option.get();
|
Files.write(path, text.getBytes());
|
||||||
if (path.isEmpty()) {
|
} catch (final IOException e) {
|
||||||
return "No path from " + word1 + " to " + word2;
|
System.err.println("Failed to write to file");
|
||||||
}
|
e.printStackTrace();
|
||||||
return graph.toDOT(r -> String.format(
|
|
||||||
"\"%s\" -> \"%s\" [label=\"%d\"%s]",
|
|
||||||
r.node1() + (path.contains(r.node1()) ? "~" : ""),
|
|
||||||
r.node2() + (path.contains(r.node2()) ? "~" : ""),
|
|
||||||
r.weight(),
|
|
||||||
(path.contains(r.node1()) && path.contains(r.node2()) &&
|
|
||||||
IntStream.range(0, path.size() - 1).anyMatch(
|
|
||||||
i -> path.get(i).equals(r.node1()) && path.get(i + 1).equals(r.node2())))
|
|
||||||
? ",color=\"red\""
|
|
||||||
: ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Double calPageRank(final String word) {
|
|
||||||
final var pageRank = graph.computePageRank(word);
|
|
||||||
return pageRank;
|
|
||||||
}
|
|
||||||
|
|
||||||
String randomWalk() {
|
|
||||||
final var text = graph.randomWalk().stream().collect(Collectors.joining(" "));
|
|
||||||
final Path path = Paths.get("output.txt");
|
|
||||||
try {
|
|
||||||
Files.write(path, text.getBytes());
|
|
||||||
} catch (final IOException e) {
|
|
||||||
System.err.println("Failed to write to file");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
kotlin-language-server
|
kotlin-language-server
|
||||||
ktfmt
|
ktfmt
|
||||||
jdt-language-server
|
jdt-language-server
|
||||||
|
checkstyle
|
||||||
|
|
||||||
graph-easy
|
graph-easy
|
||||||
graphviz
|
graphviz
|
||||||
|
|
|
||||||
188
gradlew.bat
vendored
188
gradlew.bat
vendored
|
|
@ -1,94 +1,94 @@
|
||||||
@rem
|
@rem
|
||||||
@rem Copyright 2015 the original author or authors.
|
@rem Copyright 2015 the original author or authors.
|
||||||
@rem
|
@rem
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@rem you may not use this file except in compliance with the License.
|
@rem you may not use this file except in compliance with the License.
|
||||||
@rem You may obtain a copy of the License at
|
@rem You may obtain a copy of the License at
|
||||||
@rem
|
@rem
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
@rem
|
@rem
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
@rem SPDX-License-Identifier: Apache-2.0
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@rem
|
@rem
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables with windows NT shell
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
@rem This is normally unused
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo. 1>&2
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:findJavaFromJavaHome
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo. 1>&2
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
exit /b %EXIT_CODE%
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
:omega
|
:omega
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue