Grafoscopio/repository/Grafoscopio/GrafoscopioNode.class.st

523 lines
14 KiB
Smalltalk

"
An UbakyeNode is and administrator of all node operations in a tree.
Instance Variables
node: <Object>
node
- xxxxx
"
Class {
#name : #GrafoscopioNode,
#superclass : #Object,
#instVars : [
'header',
'headers',
'key',
'icon',
'body',
'tags',
'children',
'parent',
'node',
'level',
'nodesInPreorder',
'cacheNode'
],
#category : #'Grafoscopio-Model'
}
{ #category : #'instance creation' }
GrafoscopioNode class >> header: aHeader body: aText [
"Create a new instance with given header and body"
^(self new)
header: aHeader;
body: aText;
yourself.
]
{ #category : #'instance creation' }
GrafoscopioNode class >> header: aHeader icon: anIcon body: aText [
"Create a new instances with given header, icon and body"
^(self new)
header: aHeader;
icon: anIcon;
body: aText;
yourself.
]
{ #category : #'instance creation' }
GrafoscopioNode class >> named: aString [
"Create a new instance with a given header and empty body"
^(self new)
header: aString;
yourself
]
{ #category : #'add/remove nodes' }
GrafoscopioNode >> addNode: aNode [
"Adds the given node to the receivers collection of children, and sets this object as the parent
of the node"
self children add: aNode.
aNode level: (self level) + 1.
aNode parent: self.
^aNode
]
{ #category : #'add/remove nodes' }
GrafoscopioNode >> addNodeAfter [
"Adds a generic node after the given node so they become slibings of the same parent"
| genericNode |
genericNode := GrafoscopioNode
header: 'nuevoNodo' body: ''.
self parent children add: genericNode after: self.
genericNode parent: self parent.
genericNode level: self level.
]
{ #category : #accessing }
GrafoscopioNode >> ancestors [
"Returns a collection of all the nodes wich are ancestors of the receiver node"
| currentNode ancestors |
currentNode := self.
ancestors := OrderedCollection new.
[currentNode level > 0]
whileTrue: [
ancestors add: currentNode parent.
currentNode := currentNode parent.].
ancestors := ancestors reversed.
^ ancestors
]
{ #category : #accessing }
GrafoscopioNode >> ancestorsHeaders [
"Returns the headers of all the ancestors of the node. Maybe this and 'headers' should be integrated, so both act on a collection of
children instead of having two separate methods"
| currentNode ancestors |
currentNode := self.
ancestors := OrderedCollection new.
(self level - 1)
timesRepeat: [
ancestors add: currentNode parent.
currentNode := currentNode parent.].
ancestors := ancestors reversed.
^ ancestors collect: [:ancestor | ancestor header ]
]
{ #category : #exporting }
GrafoscopioNode >> asMarkdown [
"Exports children of the current node as pandoc markdown, using special nodes accoding to tags"
| markdownOutput |
markdownOutput := '' writeStream.
(self preorderTraversal) do: [ :eachNode |
(eachNode level > 0)
ifTrue: [(eachNode hasAncestorTaggedAs: 'invisible') | (eachNode tags = 'invisible')
ifFalse: [ markdownOutput nextPutAll: (eachNode markdownContent) ]]].
^markdownOutput contents
]
{ #category : #exporting }
GrafoscopioNode >> asSton [
"Exports current tree as STON format"
| stonOutput |
stonOutput := '' writeStream.
stonOutput nextPutAll: (STON toStringPretty: self children).
^stonOutput contents
]
{ #category : #initialization }
GrafoscopioNode >> becomeDefaultTree [
| node1 |
self level: 0.
self header: 'Arbol principal'.
node1 := GrafoscopioNode
header: 'Nodo 1'
body: 'Texto 1'.
self
addNode: node1.
]
{ #category : #accessing }
GrafoscopioNode >> body [
"Returns the receivers body"
^ body
]
{ #category : #accessing }
GrafoscopioNode >> body: anObject [
"Sets the receivers body to the given object"
body := anObject
]
{ #category : #accessing }
GrafoscopioNode >> children [
"Returns the receivers list of children"
^ children ifNil: [children := OrderedCollection new]
]
{ #category : #accessing }
GrafoscopioNode >> children: aCollection [
"Sets the receivers children"
aCollection do: [:currentNode | currentNode parent: self ].
children := aCollection.
]
{ #category : #accessing }
GrafoscopioNode >> content [
"Returns the receivers body"
^ body
]
{ #category : #accessing }
GrafoscopioNode >> content: anObject [
"Sets the receivers body to the given object"
body := anObject
]
{ #category : #movement }
GrafoscopioNode >> demote [
"Moves the current node down in the hierachy, making a children of its current previous slibing"
| collection index predecessor |
collection := self parent children.
index := collection indexOf: self.
(index between: 2 and: collection size)
ifTrue: [ predecessor := collection before: self.
collection remove: self.
predecessor addNode: self]
]
{ #category : #exporting }
GrafoscopioNode >> hasAncestorHeaderWith: aSpecialWord [
"Looks if the receptor node has an ancestor with a header with 'aSpecialWord' as the only or the first word"
^ (self ancestorsHeaders includes: aSpecialWord) | ((self ancestorsHeaders collect: [:eachHeader | (eachHeader findTokens: $ ) at: 1 ]) includes: aSpecialWord)
]
{ #category : #exporting }
GrafoscopioNode >> hasAncestorTaggedAs: aSpecialWord [
"Looks if the receptor node has an ancestor with a header with 'aSpecialWord' in its tags"
self ancestors detect: [:eachAncestor | eachAncestor tags = aSpecialWord ] ifFound: [^true ] ifNone: [^false ].
]
{ #category : #accessing }
GrafoscopioNode >> header [
"Returns the receiver header"
^ header
]
{ #category : #accessing }
GrafoscopioNode >> header: anObject [
"Sets the receivers header"
header := anObject
]
{ #category : #accessing }
GrafoscopioNode >> headers [
"Returns the headers of the receiver children"
^ headers := self children collect: [:currentNode | currentNode header ]
]
{ #category : #accessing }
GrafoscopioNode >> icon [
"Returns the receivers icon"
^icon
]
{ #category : #accessing }
GrafoscopioNode >> icon: aSymbol [
"Sets the receivers icon"
icon := aSymbol
]
{ #category : #initialization }
GrafoscopioNode >> initialize [
"Creates a empty new node"
super initialize.
self header: 'newHeader'.
self body: ''.
self key: ''
]
{ #category : #accessing }
GrafoscopioNode >> key [
"Returns a unique key identifying the receiver in the help system"
^key
]
{ #category : #accessing }
GrafoscopioNode >> key: aUniqueKey [
"Sets a unique key identifying the receiver in the help system"
key := aUniqueKey
]
{ #category : #accessing }
GrafoscopioNode >> level [
"Returns the level of the node. See the setter message for details"
^level
]
{ #category : #accessing }
GrafoscopioNode >> level: anInteger [
"Sets the node level in a hierarchy. The only node with level 0 is the root node and from there levels increase
in 1 for its direct children, 2 for its grand children and so on. Silibings nodes has the same level"
level := anInteger
]
{ #category : #exporting }
GrafoscopioNode >> markdownContent [
"Extracts the markdown of a node using body as content, header as title and level as hierarchical level of the title.
If special nodes types are present, converts them into proper markup to be embedded inside markdown"
| markdown configDict specialWords embedNodes temporalBody invisibleChildren |
markdown := '' writeStream.
specialWords := #('%config' '%abstract' '%invisible' '%idea' '%footnote' 'nuevoNodo' '%embed').
(specialWords includes: self header) not & (specialWords includes: ((self header findTokens: $ ) at: 1)) not & (self tags = 'código') not
ifTrue: [
self level timesRepeat: [ markdown nextPutAll: '#' ].
markdown nextPutAll: ' '.
markdown nextPutAll: (self header copyReplaceTokens: #cr with: #lf); crlf; crlf.
embedNodes := self children select: [:each | ((each header findTokens: $ ) at: 1) = '%embed'].
temporalBody := self body.
embedNodes ifNotNil: [
(temporalBody includesSubstring: '%embed-all')
ifFalse: [embedNodes do: [ :each | temporalBody := temporalBody copyReplaceAll: (each header) with: each body]]
ifTrue: [
embedNodes do:
[ :each | temporalBody := temporalBody copyReplaceAll: '%embed-all' with: (each body,
(String with: Character cr),
'%embed-all')].
temporalBody := temporalBody copyReplaceAll: '%embed-all' with: ''
]
].
markdown nextPutAll: (temporalBody contents withInternetLineEndings ); crlf; crlf].
(self header = '%config')
ifTrue: [
configDict := STON fromString: (self body).
markdown nextPutAll: '---'; lf.
markdown nextPutAll: 'title: ', (configDict at: 'title'); lf.
markdown nextPutAll: 'author: ', ((configDict at: 'author') at: 'given'), ' ', ((configDict at: 'author') at: 'family'); lf.
markdown nextPutAll: 'bibliography: ', (configDict at: 'bibliography'); lf.
markdown nextPutAll: 'abstract: ', '|'; lf; nextPutAll: (configDict at: 'abstract'); lf.
markdown nextPutAll: '---'; lf. ].
((self header findString: '%idea') = 1)
ifTrue: [
embedNodes := self children select: [:each | ((each header findTokens: $ ) at: 1) = '%embed'].
temporalBody := self body.
embedNodes ifNotNil: [ embedNodes do: [ :each | temporalBody := temporalBody copyReplaceAll: (each header) with: each body]].
markdown nextPutAll: (temporalBody contents withUnixLineEndings); lf; lf.
].
((self header findString: '%footnote') = 1)
ifTrue: [
markdown nextPutAll: ('[^',(self header copyReplaceAll: '%footnote ' with: ''),']: ' ); lf.
markdown nextPutAll: (self body contents withInternetLineEndings); lf; lf. ].
((self header findString: '%embed') = 1)
ifTrue: [ ].
(self tags = 'invisible')
ifTrue: [
invisibleChildren := self children.
invisibleChildren ifNotNil: [ ] ].
^markdown contents
]
{ #category : #movement }
GrafoscopioNode >> moveAfter [
"Moves the current node a place before in the children collection where is located"
| collection index successor |
collection := self parent children.
index := collection indexOf: self.
(index between: 1 and: collection size - 1)
ifTrue: [
successor := collection after: self.
collection at: index + 1 put: self.
collection at: index put: successor]
]
{ #category : #movement }
GrafoscopioNode >> moveBefore [
"Moves the current node a place before in the children collection where is located"
| collection index predecessor |
collection := self parent children.
index := collection indexOf: self.
(index between: 2 and: collection size)
ifTrue: [
predecessor := collection before: self.
collection at: index -1 put: self.
collection at: index put: predecessor]
]
{ #category : #'instance creation' }
GrafoscopioNode >> newNode [
node := Dictionary newFrom: {
#header -> 'newHeadline'.
#body -> ''.
#children -> #()}.
^ node.
]
{ #category : #accessing }
GrafoscopioNode >> parent [
"Returns the parent of the current node"
^ parent
]
{ #category : #accessing }
GrafoscopioNode >> parent: aNode [
"A parent is a node that has the current node in its children"
parent := aNode
]
{ #category : #exporting }
GrafoscopioNode >> preorderTraversal [
nodesInPreorder := OrderedCollection new.
self visitedGoTo: nodesInPreorder.
^ nodesInPreorder.
]
{ #category : #movement }
GrafoscopioNode >> promote [
"Moves the current node up in the hierachy, making it a slibing of its current parent"
| collection grandparent |
collection := self parent children.
grandparent := self parent parent.
collection isNotNil & grandparent isNotNil
ifTrue: [
(grandparent children) add: self after: (self parent).
self level: (self parent) level.
self parent: grandparent.
collection remove: self.]
]
{ #category : #'add/remove nodes' }
GrafoscopioNode >> removeLastNode [
"Adds the given node to the receivers collection of children, and sets this object as the parent
of the node"
self children removeLast.
]
{ #category : #'add/remove nodes' }
GrafoscopioNode >> removeNode: aNode [
"Adds the given node to the receivers collection of children, and sets this object as the parent
of the node"
self children remove: aNode.
]
{ #category : #exporting }
GrafoscopioNode >> returnConfig [
"Detects a children node containing the configuration for the creation of the output files. If nothing is detected,
creates defaults for that. Pending:
- Verifying that file data in config node is accurate, and if not create the proper locations.
- Maybe there is a need to consider if it should run on all the tree, instead of a particular node."
| configString configNode configDict |
configNode := (self children) detect: [:nodeContent | nodeContent header = 'Config' ] ifNone: [ nil ].
"This part always enter for the nil option! MUST BE CORRECTED"
configNode isNil
ifTrue: [
configString := '{
"title": "Boostrapping para el objeto de investigación",
"author": {
"given": "Offray Vladimir",
"family": "Luna Cárdenas"
},
"file": {
"relative-path" : "U/Libertadores/Grafoscopio/",
"name": "bootstrapping-objeto-investigacion",
"formats": [
#ston,
#markdown
]
}
}'
]
ifFalse: [ configString := configNode body ].
configDict := STON fromString: configString.
^configDict
]
{ #category : #accessing }
GrafoscopioNode >> tagAs: aTag [
"Tags the recipient node with aTag. For the moment we will have only one tag. In the future we will have several and there will be rules to
know if a tag excludes others from the same node"
tags := aTag.
aTag = 'código'
ifTrue: [self body: (GTPlayPage new content: self body) content]
]
{ #category : #accessing }
GrafoscopioNode >> tags [
"Returns the receiver tags. For the moment is just one... yes silly, but will be extenden properly"
^ tags
]
{ #category : #accessing }
GrafoscopioNode >> title [
"Returns the receiver header"
^ header
]
{ #category : #'add/remove nodes' }
GrafoscopioNode >> visitedGoTo: aCollection [
"Stores the current node in a collection and recursively stores its children"
aCollection add: self.
(self children isNotEmpty) & ((self header findString: '#invisible')=1) not
ifTrue: [ (self children) do: [ :eachNode | eachNode visitedGoTo: aCollection]].
]