Merge branch release/0.2.0 into master

Demo of full-path node name
insertInPath() uses setData()
Dynamic node names
New tests following mutation testing
Tidy the Node interface
Named nodes
Update code style
Drop lombok in production (still using it in test)
This commit is contained in:
Paul Campbell 2016-05-25 11:44:51 +01:00
commit 323fba13c4
18 changed files with 2085 additions and 523 deletions

3
.gitignore vendored
View file

@ -12,3 +12,6 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
/.idea/libraries/
/.idea/workspace.xml

10
.idea/checkstyle-idea.xml Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA">
<option name="configuration">
<map>
<entry key="scan-before-checkin" value="false" />
</map>
</option>
</component>
</project>

104
.idea/codeStyleSettings.xml Normal file
View file

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="GENERATE_FINAL_LOCALS" value="true" />
<option name="GENERATE_FINAL_PARAMETERS" value="true" />
<option name="VISIBILITY" value="packageLocal" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="9999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="9999" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="org.springframework" withSubpackages="true" static="false" />
<emptyLine />
<package name="uk.ac.fife" withSubpackages="true" static="false" />
<emptyLine />
<package name="net.kemitix" withSubpackages="true" static="false" />
</value>
</option>
<option name="JD_ADD_BLANK_AFTER_PARM_COMMENTS" value="true" />
<option name="JD_ADD_BLANK_AFTER_RETURN" value="true" />
<option name="WRAP_COMMENTS" value="true" />
<JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
</JavaCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="80" />
<option name="KEEP_LINE_BREAKS" value="false" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
<option name="BLANK_LINES_AROUND_FIELD" value="1" />
<option name="BLANK_LINES_AROUND_FIELD_IN_INTERFACE" value="1" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="INDENT_CASE_FROM_SWITCH" value="false" />
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="ALIGN_MULTILINE_THROWS_LIST" value="true" />
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
<option name="SPACE_BEFORE_ANNOTATION_ARRAY_INITIALIZER_LBRACE" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="RESOURCE_LIST_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_LIST_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="5" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="5" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE" value="true" />
<option name="ASSERT_STATEMENT_WRAP" value="1" />
<option name="ASSERT_STATEMENT_COLON_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="WRAP_LONG_LINES" value="true" />
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
<option name="ENUM_CONSTANTS_WRAP" value="1" />
<arrangement>
<groups>
<group>
<type>GETTERS_AND_SETTERS</type>
<order>KEEP</order>
</group>
<group>
<type>OVERRIDDEN_METHODS</type>
<order>KEEP</order>
</group>
<group>
<type>DEPENDENT_METHODS</type>
<order>BREADTH_FIRST</order>
</group>
</groups>
</arrangement>
</codeStyleSettings>
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>

33
.idea/compiler.xml Normal file
View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
<profile default="false" name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="true" />
<module name="node" />
</profile>
</annotationProcessing>
<bytecodeTargetLevel>
<module name="node" target="1.8" />
</bytecodeTargetLevel>
</component>
</project>

7
.idea/encodings.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

213
.idea/findbugs-idea.xml Normal file
View file

@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="org.twodividedbyzero.idea.findbugs">
<option name="annotationTypeSettings">
<map>
<entry key="ExpPriority" value="-4473925;-12828863;-8355712;WAVE_UNDERSCORE;0;" />
<entry key="HighPriority" value="-39836;-12828863;-39836;WAVE_UNDERSCORE;1;" />
<entry key="IgnorePriority" value="-4473925;-12828863;-11978414;WAVE_UNDERSCORE;0;" />
<entry key="LowPriority" value="-4473925;-12828863;-10316203;BOXED;0;" />
<entry key="NormalPriority" value="-4473925;-12828863;-10461184;WAVE_UNDERSCORE;2;" />
</map>
</option>
<option name="_basePreferences">
<map>
<entry key="property.analysisEffortLevel" value="default" />
<entry key="property.analyzeAfterCompile" value="false" />
<entry key="property.annotationGutterIconEnabled" value="true" />
<entry key="property.annotationSuppressWarningsClass" value="edu.umd.cs.findbugs.annotations.SuppressWarnings" />
<entry key="property.annotationTextRangeMarkupEnabled" value="true" />
<entry key="property.exportAsHtml" value="true" />
<entry key="property.exportAsXml" value="true" />
<entry key="property.exportBaseDir" value="" />
<entry key="property.exportCreateArchiveDir" value="false" />
<entry key="property.exportOpenBrowser" value="true" />
<entry key="property.minPriorityToReport" value="Medium" />
<entry key="property.runAnalysisInBackground" value="false" />
<entry key="property.showHiddenDetectors" value="false" />
<entry key="property.toolWindowToFront" value="true" />
</map>
</option>
<option name="_detectors">
<map>
<entry key="AppendingToAnObjectOutputStream" value="true" />
<entry key="AtomicityProblem" value="true" />
<entry key="BadAppletConstructor" value="false" />
<entry key="BadResultSetAccess" value="true" />
<entry key="BadSyntaxForRegularExpression" value="true" />
<entry key="BadUseOfReturnValue" value="true" />
<entry key="BadlyOverriddenAdapter" value="true" />
<entry key="BooleanReturnNull" value="true" />
<entry key="BuildInterproceduralCallGraph" value="false" />
<entry key="BuildObligationPolicyDatabase" value="true" />
<entry key="CallToUnsupportedMethod" value="false" />
<entry key="CalledMethods" value="true" />
<entry key="CheckCalls" value="false" />
<entry key="CheckExpectedWarnings" value="false" />
<entry key="CheckImmutableAnnotation" value="true" />
<entry key="CheckTypeQualifiers" value="true" />
<entry key="CloneIdiom" value="true" />
<entry key="ComparatorIdiom" value="true" />
<entry key="ConfusedInheritance" value="true" />
<entry key="ConfusionBetweenInheritedAndOuterMethod" value="true" />
<entry key="CrossSiteScripting" value="true" />
<entry key="DefaultEncodingDetector" value="true" />
<entry key="DoInsideDoPrivileged" value="true" />
<entry key="DontCatchIllegalMonitorStateException" value="true" />
<entry key="DontIgnoreResultOfPutIfAbsent" value="true" />
<entry key="DontUseEnum" value="true" />
<entry key="DroppedException" value="true" />
<entry key="DumbMethodInvocations" value="true" />
<entry key="DumbMethods" value="true" />
<entry key="DuplicateBranches" value="true" />
<entry key="EmptyZipFileEntry" value="true" />
<entry key="EqualsOperandShouldHaveClassCompatibleWithThis" value="true" />
<entry key="ExplicitSerialization" value="true" />
<entry key="FieldItemSummary" value="true" />
<entry key="FinalizerNullsFields" value="true" />
<entry key="FindBadCast2" value="true" />
<entry key="FindBadForLoop" value="true" />
<entry key="FindBugsSummaryStats" value="true" />
<entry key="FindCircularDependencies" value="false" />
<entry key="FindDeadLocalStores" value="true" />
<entry key="FindDoubleCheck" value="true" />
<entry key="FindEmptySynchronizedBlock" value="true" />
<entry key="FindFieldSelfAssignment" value="true" />
<entry key="FindFinalizeInvocations" value="true" />
<entry key="FindFloatEquality" value="true" />
<entry key="FindFloatMath" value="false" />
<entry key="FindHEmismatch" value="true" />
<entry key="FindInconsistentSync2" value="true" />
<entry key="FindJSR166LockMonitorenter" value="true" />
<entry key="FindLocalSelfAssignment2" value="true" />
<entry key="FindMaskedFields" value="true" />
<entry key="FindMismatchedWaitOrNotify" value="true" />
<entry key="FindNakedNotify" value="true" />
<entry key="FindNonSerializableStoreIntoSession" value="false" />
<entry key="FindNonSerializableValuePassedToWriteObject" value="false" />
<entry key="FindNonShortCircuit" value="true" />
<entry key="FindNullDeref" value="true" />
<entry key="FindNullDerefsInvolvingNonShortCircuitEvaluation" value="true" />
<entry key="FindOpenStream" value="true" />
<entry key="FindPuzzlers" value="true" />
<entry key="FindRefComparison" value="true" />
<entry key="FindReturnRef" value="true" />
<entry key="FindRunInvocations" value="true" />
<entry key="FindSelfComparison" value="true" />
<entry key="FindSelfComparison2" value="true" />
<entry key="FindSleepWithLockHeld" value="true" />
<entry key="FindSpinLoop" value="true" />
<entry key="FindSqlInjection" value="true" />
<entry key="FindTwoLockWait" value="true" />
<entry key="FindUncalledPrivateMethods" value="true" />
<entry key="FindUnconditionalWait" value="true" />
<entry key="FindUninitializedGet" value="true" />
<entry key="FindUnrelatedTypesInGenericContainer" value="true" />
<entry key="FindUnreleasedLock" value="true" />
<entry key="FindUnsatisfiedObligation" value="true" />
<entry key="FindUnsyncGet" value="true" />
<entry key="FindUseOfNonSerializableValue" value="true" />
<entry key="FindUselessControlFlow" value="true" />
<entry key="FormatStringChecker" value="true" />
<entry key="FunctionsThatMightBeMistakenForProcedures" value="true" />
<entry key="HugeSharedStringConstants" value="true" />
<entry key="IDivResultCastToDouble" value="true" />
<entry key="IncompatMask" value="true" />
<entry key="InconsistentAnnotations" value="true" />
<entry key="InefficientMemberAccess" value="false" />
<entry key="InefficientToArray" value="true" />
<entry key="InfiniteLoop" value="true" />
<entry key="InfiniteRecursiveLoop" value="true" />
<entry key="InheritanceUnsafeGetResource" value="true" />
<entry key="InitializationChain" value="true" />
<entry key="InitializeNonnullFieldsInConstructor" value="true" />
<entry key="InstantiateStaticClass" value="true" />
<entry key="IntCast2LongAsInstant" value="true" />
<entry key="InvalidJUnitTest" value="true" />
<entry key="IteratorIdioms" value="true" />
<entry key="LazyInit" value="true" />
<entry key="LoadOfKnownNullValue" value="true" />
<entry key="LostLoggerDueToWeakReference" value="true" />
<entry key="MethodReturnCheck" value="true" />
<entry key="Methods" value="true" />
<entry key="MultithreadedInstanceAccess" value="true" />
<entry key="MutableLock" value="true" />
<entry key="MutableStaticFields" value="true" />
<entry key="Naming" value="true" />
<entry key="Noise" value="false" />
<entry key="NoiseNullDeref" value="false" />
<entry key="NoteAnnotationRetention" value="true" />
<entry key="NoteCheckReturnValueAnnotations" value="true" />
<entry key="NoteDirectlyRelevantTypeQualifiers" value="true" />
<entry key="NoteJCIPAnnotation" value="true" />
<entry key="NoteNonNullAnnotations" value="true" />
<entry key="NoteNonnullReturnValues" value="true" />
<entry key="NoteSuppressedWarnings" value="true" />
<entry key="NoteUnconditionalParamDerefs" value="true" />
<entry key="NumberConstructor" value="true" />
<entry key="OverridingEqualsNotSymmetrical" value="true" />
<entry key="PreferZeroLengthArrays" value="true" />
<entry key="PublicSemaphores" value="false" />
<entry key="QuestionableBooleanAssignment" value="true" />
<entry key="ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass" value="true" />
<entry key="ReadReturnShouldBeChecked" value="true" />
<entry key="RedundantInterfaces" value="true" />
<entry key="ReflectiveClasses" value="true" />
<entry key="RepeatedConditionals" value="true" />
<entry key="ResolveAllReferences" value="false" />
<entry key="RuntimeExceptionCapture" value="true" />
<entry key="SerializableIdiom" value="true" />
<entry key="StartInConstructor" value="true" />
<entry key="StaticCalendarDetector" value="true" />
<entry key="StringConcatenation" value="true" />
<entry key="SuperfluousInstanceOf" value="true" />
<entry key="SuspiciousThreadInterrupted" value="true" />
<entry key="SwitchFallthrough" value="true" />
<entry key="SynchronizationOnSharedBuiltinConstant" value="true" />
<entry key="SynchronizeAndNullCheckField" value="true" />
<entry key="SynchronizeOnClassLiteralNotGetClass" value="true" />
<entry key="SynchronizingOnContentsOfFieldToProtectField" value="true" />
<entry key="TestASM" value="false" />
<entry key="TestDataflowAnalysis" value="false" />
<entry key="TestingGround" value="false" />
<entry key="TestingGround2" value="false" />
<entry key="TrainFieldStoreTypes" value="true" />
<entry key="TrainLongInstantfParams" value="true" />
<entry key="TrainNonNullAnnotations" value="true" />
<entry key="TrainUnconditionalDerefParams" value="true" />
<entry key="URLProblems" value="true" />
<entry key="UncallableMethodOfAnonymousClass" value="true" />
<entry key="UnnecessaryMath" value="true" />
<entry key="UnreadFields" value="true" />
<entry key="UselessSubclassMethod" value="false" />
<entry key="VarArgsProblems" value="true" />
<entry key="VolatileUsage" value="true" />
<entry key="WaitInLoop" value="true" />
<entry key="WrongMapIterator" value="true" />
<entry key="XMLFactoryBypass" value="true" />
</map>
</option>
<option name="_reportCategories">
<map>
<entry key="BAD_PRACTICE" value="true" />
<entry key="CORRECTNESS" value="true" />
<entry key="EXPERIMENTAL" value="true" />
<entry key="I18N" value="true" />
<entry key="MALICIOUS_CODE" value="true" />
<entry key="MT_CORRECTNESS" value="true" />
<entry key="PERFORMANCE" value="true" />
<entry key="SECURITY" value="true" />
<entry key="STYLE" value="true" />
</map>
</option>
<option name="_annotationTypeSettings">
<map>
<entry key="ExpPriority" value="-4473925;-12828863;-8355712;WAVE_UNDERSCORE;0;" />
<entry key="HighPriority" value="-39836;-12828863;-39836;WAVE_UNDERSCORE;1;" />
<entry key="IgnorePriority" value="-4473925;-12828863;-11978414;WAVE_UNDERSCORE;0;" />
<entry key="LowPriority" value="-4473925;-12828863;-10316203;BOXED;0;" />
<entry key="NormalPriority" value="-4473925;-12828863;-10461184;WAVE_UNDERSCORE;2;" />
</map>
</option>
</component>
</project>

View file

@ -0,0 +1,60 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<option name="myLocal" value="true" />
<inspection_tool class="AssertEqualsCalledOnArray" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertEqualsMayBeAssertSame" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertsWithoutMessages" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BeforeClassOrAfterClassIsPublicStaticVoidNoArg" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BeforeOrAfterIsPublicVoidNoArg" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantJUnitAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExpectedExceptionNeverThrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FindBugsIDEA" enabled="true" level="SERVER PROBLEM" enabled_by_default="true" />
<inspection_tool class="IgnoredJUnitTest" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnit3MethodNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnit3StyleTestMethodInJUnit4Class" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnit4MethodNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnitAbstractTestClassNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[A-Z][A-Za-z\d]*TestCase" />
<option name="m_minLength" value="12" />
<option name="m_maxLength" value="64" />
</inspection_tool>
<inspection_tool class="JUnitDatapoint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnitRule" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnitTestClassNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[A-Z][A-Za-z\d]*(Test|IT)" />
<option name="m_minLength" value="8" />
<option name="m_maxLength" value="64" />
</inspection_tool>
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
<option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
</inspection_tool>
<inspection_tool class="MigrateAssertToMatcherAssert" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisorderedAssertEqualsParameters" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisspelledSetUp" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisspelledTearDown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MultipleExceptionsDeclaredOnTestMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ParameterizedParametersStaticCollection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SetupCallsSuperSetup" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SetupIsPublicVoidNoArg" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SimplifiableJUnitAssertion" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticSuite" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuperTearDownInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TeardownCallsSuperTeardown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TeardownIsPublicVoidNoArg" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestCaseInProductCode" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestCaseWithConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestCaseWithNoTestMethods" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSupers" value="false" />
</inspection_tool>
<inspection_tool class="TestMethodInProductCode" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestMethodIsPublicVoidNoArg" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestMethodWithoutAssertion" enabled="true" level="WARNING" enabled_by_default="true">
<option name="assertionMethods" value="org.junit.Assert,assert.*|fail.*,junit.framework.Assert,assert.*|fail.*,org.mockito.Mockito,verify.*,org.mockito.InOrder,verify,org.junit.rules.ExpectedException,expect.*,org.hamcrest.MatcherAssert,assertThat,org.assertj.core.api.Assertions,assertThat,org.assertj.core.api.SoftAssertions,assertAll" />
<option name="assertKeywordIsAssertion" value="false" />
</inspection_tool>
<inspection_tool class="UnconstructableTestCase" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfObsoleteAssert" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View file

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

42
.idea/misc.xml Normal file
View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" />
</component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.8</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/node.iml" filepath="$PROJECT_DIR$/node.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -1,6 +1,18 @@
CHANGELOG
=========
0.2.0
------
* Demo of full-path node name
* insertInPath() uses setData()
* Dynamic node names
* New tests following mutation testing
* Tidy the Node interface
* Named nodes
* Update code style
* Drop lombok in production (still using it in test)
0.1.0
------

180
node.iml Normal file
View file

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="Maven: org.projectlombok:lombok:1.16.8" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:3.4.1" level="project" />
</component>
<component name="org.twodividedbyzero.idea.findbugs">
<option name="_detectors">
<map>
<entry key="AppendingToAnObjectOutputStream" value="true" />
<entry key="AtomicityProblem" value="true" />
<entry key="BadAppletConstructor" value="false" />
<entry key="BadResultSetAccess" value="true" />
<entry key="BadSyntaxForRegularExpression" value="true" />
<entry key="BadUseOfReturnValue" value="true" />
<entry key="BadlyOverriddenAdapter" value="true" />
<entry key="BooleanReturnNull" value="true" />
<entry key="BuildInterproceduralCallGraph" value="false" />
<entry key="BuildObligationPolicyDatabase" value="true" />
<entry key="CallToUnsupportedMethod" value="false" />
<entry key="CalledMethods" value="true" />
<entry key="CheckCalls" value="false" />
<entry key="CheckExpectedWarnings" value="false" />
<entry key="CheckImmutableAnnotation" value="true" />
<entry key="CheckTypeQualifiers" value="true" />
<entry key="CloneIdiom" value="true" />
<entry key="ComparatorIdiom" value="true" />
<entry key="ConfusedInheritance" value="true" />
<entry key="ConfusionBetweenInheritedAndOuterMethod" value="true" />
<entry key="CrossSiteScripting" value="true" />
<entry key="DefaultEncodingDetector" value="true" />
<entry key="DoInsideDoPrivileged" value="true" />
<entry key="DontCatchIllegalMonitorStateException" value="true" />
<entry key="DontIgnoreResultOfPutIfAbsent" value="true" />
<entry key="DontUseEnum" value="true" />
<entry key="DroppedException" value="true" />
<entry key="DumbMethodInvocations" value="true" />
<entry key="DumbMethods" value="true" />
<entry key="DuplicateBranches" value="true" />
<entry key="EmptyZipFileEntry" value="true" />
<entry key="EqualsOperandShouldHaveClassCompatibleWithThis" value="true" />
<entry key="ExplicitSerialization" value="true" />
<entry key="FieldItemSummary" value="true" />
<entry key="FinalizerNullsFields" value="true" />
<entry key="FindBadCast2" value="true" />
<entry key="FindBadForLoop" value="true" />
<entry key="FindBugsSummaryStats" value="true" />
<entry key="FindCircularDependencies" value="false" />
<entry key="FindDeadLocalStores" value="true" />
<entry key="FindDoubleCheck" value="true" />
<entry key="FindEmptySynchronizedBlock" value="true" />
<entry key="FindFieldSelfAssignment" value="true" />
<entry key="FindFinalizeInvocations" value="true" />
<entry key="FindFloatEquality" value="true" />
<entry key="FindFloatMath" value="false" />
<entry key="FindHEmismatch" value="true" />
<entry key="FindInconsistentSync2" value="true" />
<entry key="FindJSR166LockMonitorenter" value="true" />
<entry key="FindLocalSelfAssignment2" value="true" />
<entry key="FindMaskedFields" value="true" />
<entry key="FindMismatchedWaitOrNotify" value="true" />
<entry key="FindNakedNotify" value="true" />
<entry key="FindNonSerializableStoreIntoSession" value="false" />
<entry key="FindNonSerializableValuePassedToWriteObject" value="false" />
<entry key="FindNonShortCircuit" value="true" />
<entry key="FindNullDeref" value="true" />
<entry key="FindNullDerefsInvolvingNonShortCircuitEvaluation" value="true" />
<entry key="FindOpenStream" value="true" />
<entry key="FindPuzzlers" value="true" />
<entry key="FindRefComparison" value="true" />
<entry key="FindReturnRef" value="true" />
<entry key="FindRunInvocations" value="true" />
<entry key="FindSelfComparison" value="true" />
<entry key="FindSelfComparison2" value="true" />
<entry key="FindSleepWithLockHeld" value="true" />
<entry key="FindSpinLoop" value="true" />
<entry key="FindSqlInjection" value="true" />
<entry key="FindTwoLockWait" value="true" />
<entry key="FindUncalledPrivateMethods" value="true" />
<entry key="FindUnconditionalWait" value="true" />
<entry key="FindUninitializedGet" value="true" />
<entry key="FindUnrelatedTypesInGenericContainer" value="true" />
<entry key="FindUnreleasedLock" value="true" />
<entry key="FindUnsatisfiedObligation" value="true" />
<entry key="FindUnsyncGet" value="true" />
<entry key="FindUseOfNonSerializableValue" value="true" />
<entry key="FindUselessControlFlow" value="true" />
<entry key="FormatStringChecker" value="true" />
<entry key="FunctionsThatMightBeMistakenForProcedures" value="true" />
<entry key="HugeSharedStringConstants" value="true" />
<entry key="IDivResultCastToDouble" value="true" />
<entry key="IncompatMask" value="true" />
<entry key="InconsistentAnnotations" value="true" />
<entry key="InefficientMemberAccess" value="false" />
<entry key="InefficientToArray" value="true" />
<entry key="InfiniteLoop" value="true" />
<entry key="InfiniteRecursiveLoop" value="true" />
<entry key="InheritanceUnsafeGetResource" value="true" />
<entry key="InitializationChain" value="true" />
<entry key="InitializeNonnullFieldsInConstructor" value="true" />
<entry key="InstantiateStaticClass" value="true" />
<entry key="IntCast2LongAsInstant" value="true" />
<entry key="InvalidJUnitTest" value="true" />
<entry key="IteratorIdioms" value="true" />
<entry key="LazyInit" value="true" />
<entry key="LoadOfKnownNullValue" value="true" />
<entry key="LostLoggerDueToWeakReference" value="true" />
<entry key="MethodReturnCheck" value="true" />
<entry key="Methods" value="true" />
<entry key="MultithreadedInstanceAccess" value="true" />
<entry key="MutableLock" value="true" />
<entry key="MutableStaticFields" value="true" />
<entry key="Naming" value="true" />
<entry key="Noise" value="false" />
<entry key="NoiseNullDeref" value="false" />
<entry key="NoteAnnotationRetention" value="true" />
<entry key="NoteCheckReturnValueAnnotations" value="true" />
<entry key="NoteDirectlyRelevantTypeQualifiers" value="true" />
<entry key="NoteJCIPAnnotation" value="true" />
<entry key="NoteNonNullAnnotations" value="true" />
<entry key="NoteNonnullReturnValues" value="true" />
<entry key="NoteSuppressedWarnings" value="true" />
<entry key="NoteUnconditionalParamDerefs" value="true" />
<entry key="NumberConstructor" value="true" />
<entry key="OverridingEqualsNotSymmetrical" value="true" />
<entry key="PreferZeroLengthArrays" value="true" />
<entry key="PublicSemaphores" value="false" />
<entry key="QuestionableBooleanAssignment" value="true" />
<entry key="ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass" value="true" />
<entry key="ReadReturnShouldBeChecked" value="true" />
<entry key="RedundantInterfaces" value="true" />
<entry key="ReflectiveClasses" value="true" />
<entry key="RepeatedConditionals" value="true" />
<entry key="ResolveAllReferences" value="false" />
<entry key="RuntimeExceptionCapture" value="true" />
<entry key="SerializableIdiom" value="true" />
<entry key="StartInConstructor" value="true" />
<entry key="StaticCalendarDetector" value="true" />
<entry key="StringConcatenation" value="true" />
<entry key="SuperfluousInstanceOf" value="true" />
<entry key="SuspiciousThreadInterrupted" value="true" />
<entry key="SwitchFallthrough" value="true" />
<entry key="SynchronizationOnSharedBuiltinConstant" value="true" />
<entry key="SynchronizeAndNullCheckField" value="true" />
<entry key="SynchronizeOnClassLiteralNotGetClass" value="true" />
<entry key="SynchronizingOnContentsOfFieldToProtectField" value="true" />
<entry key="TestASM" value="false" />
<entry key="TestDataflowAnalysis" value="false" />
<entry key="TestingGround" value="false" />
<entry key="TestingGround2" value="false" />
<entry key="TrainFieldStoreTypes" value="true" />
<entry key="TrainLongInstantfParams" value="true" />
<entry key="TrainNonNullAnnotations" value="true" />
<entry key="TrainUnconditionalDerefParams" value="true" />
<entry key="URLProblems" value="true" />
<entry key="UncallableMethodOfAnonymousClass" value="true" />
<entry key="UnnecessaryMath" value="true" />
<entry key="UnreadFields" value="true" />
<entry key="UselessSubclassMethod" value="false" />
<entry key="VarArgsProblems" value="true" />
<entry key="VolatileUsage" value="true" />
<entry key="WaitInLoop" value="true" />
<entry key="WrongMapIterator" value="true" />
<entry key="XMLFactoryBypass" value="true" />
</map>
</option>
</component>
</module>

17
pom.xml
View file

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.kemitix</groupId>
<artifactId>node</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
<packaging>jar</packaging>
<name>Node</name>
@ -15,6 +14,10 @@
<version>0.6.0</version>
</parent>
<properties>
<assertj.version>3.4.1</assertj.version>
</properties>
<issueManagement>
<url>https://github.com/kemitix/node/issues</url>
<system>GitHub Issues</system>
@ -34,9 +37,9 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
<version>1.16.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@ -49,5 +52,11 @@
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -7,11 +7,28 @@ import java.util.Set;
/**
* An interface for tree node items.
*
* @author pcampbell
* @param <T> the type of data held in each node
*
* @author pcampbell
*/
public interface Node<T> {
/**
* Fetch the name of the node. Where a node's name is determined via a name
* supplier, the name may be regenerated each time this method is called.
*
* @return the name of the node
*/
String getName();
/**
* Sets the explicit name for a node. Setting the name to null will clear
* the name and revert to the parent's name supplier.
*
* @param name the new name
*/
void setName(String name);
/**
* Fetch the data held within the node.
*
@ -20,8 +37,21 @@ public interface Node<T> {
T getData();
/**
* Fetch the parent node.
* Set the data held within the node.
*
* @param data the node's data
*/
void setData(T data);
/**
* Returns true if the node is empty (has no data).
*
* @return true is data is null
*/
boolean isEmpty();
/**
* Fetch the parent node.
* <p>
* If the node is a root node, i.e. has no parent, then this will return
* null.
@ -30,6 +60,13 @@ public interface Node<T> {
*/
Node<T> getParent();
/**
* Make the current node a direct child of the parent.
*
* @param parent the new parent node
*/
void setParent(Node<T> parent);
/**
* Fetches the child nodes.
*
@ -42,16 +79,26 @@ public interface Node<T> {
*
* @param child the node to add
*/
void addChild(final Node<T> child);
void addChild(Node<T> child);
/**
* Creates a new node and adds it as a child of the current node.
* Creates a new unnamed node and adds it as a child of the current node.
*
* @param child the child node's data
*
* @return the new child node
*/
Node<T> createChild(final T child);
Node<T> createChild(T child);
/**
* Creates a new named node and adds it as a child of the current node.
*
* @param child the child node's data
* @param name the name
*
* @return the new child node
*/
Node<T> createChild(T child, String name);
/**
* Populates the tree with the path of nodes, each being a child of the
@ -59,7 +106,7 @@ public interface Node<T> {
*
* @param descendants the line of descendants from the current node
*/
void createDescendantLine(final List<T> descendants);
void createDescendantLine(List<T> descendants);
/**
* Looks for a child node and returns it, creating a new child node if one
@ -69,7 +116,8 @@ public interface Node<T> {
*
* @return the found or created child node
*/
Node<T> findOrCreateChild(final T child);
@Deprecated
Node<T> findOrCreateChild(T child);
/**
* Fetches the node for the child if present.
@ -78,7 +126,18 @@ public interface Node<T> {
*
* @return an {@link Optional} containing the child node if found
*/
Optional<Node<T>> getChild(final T child);
Optional<Node<T>> findChild(T child);
/**
* Fetches the node for the child if present.
*
* @param child the child's data to search for
*
* @return the child node if found
*
* @throws NodeException if the node is not found
*/
Node<T> getChild(T child);
/**
* Checks if the node is an ancestor.
@ -87,14 +146,7 @@ public interface Node<T> {
*
* @return true if the node is an ancestor
*/
boolean isChildOf(final Node<T> node);
/**
* Make the current node a direct child of the parent.
*
* @param parent the new parent node
*/
void setParent(final Node<T> parent);
boolean isDescendantOf(Node<T> node);
/**
* Walks the node tree using the path to select each child.
@ -103,6 +155,62 @@ public interface Node<T> {
*
* @return the child or null
*/
Optional<Node<T>> walkTree(final List<T> path);
Optional<Node<T>> findInPath(List<T> path);
/**
* Places the node in the tree under by the path. Intervening empty
* nodes are created as needed.
*
* @param node the node to place
* @param path the path to contain the new node
*/
void insertInPath(Node<T> node, String... path);
/**
* Searches for a child with the name given.
*
* @param name the name of the child
*
* @return an Optional containing the child found or empty
*/
Optional<Node<T>> findChildByName(String name);
/**
* Returns the child with the given name. If one can't be found a
* NodeException is thrown.
*
* @param name the name of the child
*
* @return the node
*/
Node<T> getChildByName(String name);
/**
* Draw a representation of the tree.
*
* @param depth current depth for recursion
*
* @return a representation of the tree
*/
String drawTree(int depth);
/**
* Returns true if the Node has a name. Where a name supplier is used, the
* generated name is used.
*
* @return true if the node has a name
*/
boolean isNamed();
/**
* Remove the node from the children.
*
* @param node the node to be removed
*/
void removeChild(Node<T> node);
/**
* Removes the parent from the node. Makes the node into a new root node.
*/
void removeParent();
}

View file

@ -1,12 +1,11 @@
package net.kemitix.node;
import lombok.Getter;
import lombok.NonNull;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
/**
* Represents a tree of nodes.
@ -17,53 +16,136 @@ import java.util.Set;
*/
public class NodeItem<T> implements Node<T> {
@Getter
private final T data;
private T data;
private final Set<Node<T>> children = new HashSet<>();
private Function<Node<T>, String> nameSupplier;
@Getter
private Node<T> parent;
@Getter
private Set<Node<T>> children;
private String name;
/**
* Creates a root node.
* Create named root node.
*
* @param data the value of the node
* @param data the data or null
* @param name the name
*/
public NodeItem(@NonNull final T data) {
this(data, null);
public NodeItem(final T data, final String name) {
this(data);
this.name = name;
}
/**
* Create unnamed root node.
*
* @param data the data or null
*/
public NodeItem(final T data) {
this.data = data;
this.nameSupplier = (n) -> null;
}
/**
* Creates root node with a name supplier.
*
* @param data the data or null
* @param nameSupplier the name supplier function
*/
public NodeItem(
final T data, final Function<Node<T>, String> nameSupplier) {
this(data);
this.nameSupplier = nameSupplier;
}
/**
* Creates a node with a parent.
*
* @param data the value of the node
* @param data the data or null
* @param parent the parent node
*/
public NodeItem(final T data, final Node<T> parent) {
this.data = data;
if (parent != null) {
setParent(parent);
}
this.children = new HashSet<>();
setParent(parent);
}
/**
* Make the current node a direct child of the parent.
* Creates a named node with a parent.
*
* @param parent the new parent node
* @param data the data or null
* @param name the name
* @param parent the parent node
*/
public NodeItem(final T data, final String name, final Node<T> parent) {
this.data = data;
this.name = name;
setParent(parent);
}
/**
* Creates a node with a name supplier and a parent.
*
* @param data the data or null
* @param nameSupplier the name supplier function
* @param parent the parent node
*/
public NodeItem(
final T data, final Function<Node<T>, String> nameSupplier,
final Node<T> parent) {
this(data, nameSupplier);
setParent(parent);
}
private String generateName() {
return getNameSupplier().apply(this);
}
private Function<Node<T>, String> getNameSupplier() {
if (nameSupplier != null) {
return nameSupplier;
}
// no test for parent as root nodes will always have a default name
// supplier
return ((NodeItem<T>) parent).getNameSupplier();
}
@Override
public final void setParent(@NonNull final Node<T> parent) {
if (this.equals(parent) || parent.isChildOf(this)) {
throw new NodeException("Parent is a descendant");
public String getName() {
if (name == null) {
return generateName();
}
if (this.parent != null) {
this.parent.getChildren().remove(this);
}
this.parent = parent;
parent.addChild(this);
return name;
}
@Override
public void setName(final String name) {
this.name = name;
}
@Override
public T getData() {
return data;
}
@Override
public void setData(final T data) {
this.data = data;
}
@Override
public boolean isEmpty() {
return data == null;
}
@Override
public Node<T> getParent() {
return parent;
}
@Override
public Set<Node<T>> getChildren() {
return children;
}
/**
@ -72,10 +154,21 @@ public class NodeItem<T> implements Node<T> {
* @param child the node to add
*/
@Override
public void addChild(@NonNull final Node<T> child) {
if (this.equals(child) || isChildOf(child)) {
public void addChild(final Node<T> child) {
if (child == null) {
throw new NullPointerException("child");
}
if (this.equals(child) || isDescendantOf(child)) {
throw new NodeException("Child is an ancestor");
}
if (child.isNamed()) {
final Optional<Node<T>> existingChild = findChildByName(
child.getName());
if (existingChild.isPresent() && existingChild.get() != child) {
throw new NodeException(
"Node with that name already exists here");
}
}
children.add(child);
if (child.getParent() == null || !child.getParent().equals(this)) {
child.setParent(this);
@ -83,45 +176,26 @@ public class NodeItem<T> implements Node<T> {
}
/**
* Checks if the node is an ancestor.
* Creates a new node and adds it as a child of the current node.
*
* @param node the potential ancestor
* @param child the child node's data
*
* @return true if the node is an ancestor
* @return the new child node
*/
@Override
public boolean isChildOf(final Node<T> node) {
if (node.equals(parent)) {
return true;
public Node<T> createChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
}
if (parent != null) {
return parent.isChildOf(node);
}
return false;
return new NodeItem<>(child, this);
}
/**
* Walks the node tree using the path to select each child.
*
* @param path the path to the desired child
*
* @return the child or null
*/
@Override
public Optional<Node<T>> walkTree(@NonNull final List<T> path) {
if (path.size() > 0) {
Optional<Node<T>> found = children.stream()
.filter((Node<T> child) -> path.get(0)
.equals(child.getData()))
.findFirst();
if (found.isPresent()) {
if (path.size() > 1) {
return found.get().walkTree(path.subList(1, path.size()));
}
return found;
}
}
return Optional.empty();
@SuppressWarnings("hiddenfield")
public Node<T> createChild(final T child, final String name) {
Node<T> node = createChild(child);
node.setName(name);
return node;
}
/**
@ -131,11 +205,13 @@ public class NodeItem<T> implements Node<T> {
* @param descendants the line of descendants from the current node
*/
@Override
public void createDescendantLine(@NonNull final List<T> descendants) {
public void createDescendantLine(final List<T> descendants) {
if (descendants == null) {
throw new NullPointerException("descendants");
}
if (!descendants.isEmpty()) {
findOrCreateChild(descendants.get(0))
.createDescendantLine(
descendants.subList(1, descendants.size()));
findOrCreateChild(descendants.get(0)).createDescendantLine(
descendants.subList(1, descendants.size()));
}
}
@ -148,13 +224,11 @@ public class NodeItem<T> implements Node<T> {
* @return the found or created child node
*/
@Override
public Node<T> findOrCreateChild(@NonNull final T child) {
Optional<Node<T>> found = getChild(child);
if (found.isPresent()) {
return found.get();
} else {
return createChild(child);
public Node<T> findOrCreateChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
}
return findChild(child).orElseGet(() -> createChild(child));
}
/**
@ -165,22 +239,176 @@ public class NodeItem<T> implements Node<T> {
* @return an {@link Optional} containing the child node if found
*/
@Override
public Optional<Node<T>> getChild(@NonNull final T child) {
public Optional<Node<T>> findChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
}
return children.stream()
.filter((Node<T> t) -> t.getData().equals(child))
.findAny();
.filter((Node<T> t) -> t.getData().equals(child))
.findAny();
}
@Override
public Node<T> getChild(final T child) {
Optional<Node<T>> optional = findChild(child);
if (optional.isPresent()) {
return optional.get();
}
throw new NodeException("Child not found");
}
/**
* Creates a new node and adds it as a child of the current node.
* Checks if the node is an ancestor.
*
* @param child the child node's data
* @param node the potential ancestor
*
* @return the new child node
* @return true if the node is an ancestor
*/
@Override
public Node<T> createChild(@NonNull final T child) {
return new NodeItem<>(child, this);
public boolean isDescendantOf(final Node<T> node) {
return parent != null && (node.equals(parent) || parent.isDescendantOf(
node));
}
/**
* Make the current node a direct child of the parent.
*
* @param parent the new parent node
*/
@Override
public final void setParent(final Node<T> parent) {
if (parent == null) {
throw new NullPointerException("parent");
}
if (this.equals(parent) || parent.isDescendantOf(this)) {
throw new NodeException("Parent is a descendant");
}
if (this.parent != null) {
this.parent.getChildren().remove(this);
}
this.parent = parent;
parent.addChild(this);
}
/**
* Walks the node tree using the path to select each child.
*
* @param path the path to the desired child
*
* @return the child or null
*/
@Override
public Optional<Node<T>> findInPath(final List<T> path) {
if (path == null) {
throw new NullPointerException("path");
}
if (path.size() > 0) {
Optional<Node<T>> found = findChild(path.get(0));
if (found.isPresent()) {
if (path.size() > 1) {
return found.get().findInPath(path.subList(1, path.size()));
}
return found;
}
}
return Optional.empty();
}
@Override
public void insertInPath(final Node<T> nodeItem, final String... path) {
if (path.length == 0) {
if (!nodeItem.isNamed()) { // nothing to conflict with
addChild(nodeItem);
return;
}
final Optional<Node<T>> childNamed = findChildByName(
nodeItem.getName());
if (!childNamed.isPresent()) { // nothing with the same name exists
addChild(nodeItem);
return;
}
// we have an existing node with the same name
final Node<T> existing = childNamed.get();
if (!existing.isEmpty()) {
throw new NodeException(
"A non-empty node with that name already exists here");
} else {
existing.setData(nodeItem.getData());
}
return;
}
String item = path[0];
final Optional<Node<T>> childNamed = findChildByName(item);
Node<T> child;
if (!childNamed.isPresent()) {
child = new NodeItem<>(null, item, this);
} else {
child = childNamed.get();
}
child.insertInPath(nodeItem, Arrays.copyOfRange(path, 1, path.length));
}
@Override
public Optional<Node<T>> findChildByName(final String named) {
if (named == null) {
throw new NullPointerException("name");
}
return children.stream()
.filter((Node<T> t) -> t.getName().equals(named))
.findAny();
}
@Override
public Node<T> getChildByName(final String named) {
final Optional<Node<T>> optional = findChildByName(named);
if (optional.isPresent()) {
return optional.get();
}
throw new NodeException("Named child not found");
}
@Override
public String drawTree(final int depth) {
final StringBuilder sb = new StringBuilder();
final String unnamed = "(unnamed)";
if (isNamed()) {
sb.append(String.format("[%1$" + (depth + name.length()) + "s]\n",
name));
} else if (!children.isEmpty()) {
sb.append(
String.format("[%1$" + (depth + unnamed.length()) + "s]\n",
unnamed));
}
getChildren().stream().forEach(c -> sb.append(c.drawTree(depth + 1)));
return sb.toString();
}
@Override
public boolean isNamed() {
String currentName = getName();
return currentName != null && currentName.length() > 0;
}
@Override
public void removeChild(final Node<T> node) {
if (children.remove(node)) {
node.removeParent();
}
}
@Override
public void removeParent() {
if (parent != null) {
Node<T> oldParent = parent;
Function<Node<T>, String> supplier = getNameSupplier();
parent = null;
oldParent.removeChild(this);
if (this.nameSupplier == null) {
// this is now a root node, so must provide a default name
// supplier
this.nameSupplier = supplier;
}
}
}
}

View file

@ -1,7 +1,6 @@
package net.kemitix.node;
import net.kemitix.node.NodeException;
import lombok.val;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
@ -14,20 +13,15 @@ import static org.junit.Assert.assertThat;
*/
public class NodeExceptionTest {
/**
* Class under test.
*/
private NodeException nodeException;
/**
* Test that message provided to constructor is returned.
*/
@Test
public void shouldReturnConstructorMessage() {
//given
final String message = "this is the message";
val message = "this is the message";
//when
nodeException = new NodeException(message);
val nodeException = new NodeException(message);
//then
assertThat(nodeException.getMessage(), is(message));
}

File diff suppressed because it is too large Load diff