import model
import doxml
+import graph_store
import console
import opts
# An importation task.
class NeoDoxygenJob
- var client: Neo4jClient
- var model: ProjectGraph is noinit
- # How many operation can be executed in one batch?
- private var batch_max_size = 1000
+ # The storage medium to use.
+ var store: GraphStore
+
+ # The loaded project graph.
+ var model: ProjectGraph is noinit
- private var save_cursor: String = (new TermSaveCursor).to_s
+ # Escape control sequence to save the cursor position.
+ private var term_save_cursor: String = (new TermSaveCursor).to_s
- # Escape control sequence to reset the current line.
- private var reset_line: String = "{new TermRestoreCursor}{new TermEraseDisplayDown}"
+ # Escape control sequence to rewind to the last saved cursor position.
+ private var term_rewind: String = "{new TermRestoreCursor}{new TermEraseDisplayDown}"
# Generate a graph from the specified project model.
#
var file_count = 0
if dir == "" then
- sys.stdout.write "Reading the current directory... "
+ printn "Reading the current directory... "
else
- sys.stdout.write "Reading {dir}... "
+ printn "Reading {dir}... "
end
loop
- for f in dir.files do
+ for f in list_files(dir) do
var path = dir/f
if path.file_stat.is_dir then
directories.push(path)
if directories.length <= 0 then break
dir = directories.pop
end
+ model.add_global_modules
print "Done."
if file_count < 2 then
print "{file_count} file read."
end
end
+ # List files in a directory.
+ #
+ # This method may be redefined to force the order in which the files
+ # are read by `load_project`.
+ protected fun list_files(dir: String): Collection[String] do
+ return dir.files
+ end
+
# Check the project’s name.
private fun check_name(name: String) do
assert name_valid: not name.chars.first.is_upper else
sys.stderr.write("{sys.program_name}: The project’s name must not" +
" begin with an upper case letter. Got `{name}`.\n")
end
- var query = new CypherQuery.from_string("match n where \{name\} in labels(n) return count(n)")
- query.params["name"] = name
- var data = client.cypher(query).as(JsonObject)["data"]
- var result = data.as(JsonArray).first.as(JsonArray).first.as(Int)
- assert name_unused: result == 0 else
+ assert name_unused: not store.has_node_label(name) else
sys.stderr.write("{sys.program_name}: The label `{name}` is already" +
" used in the specified graph.\n")
end
# Save the graph.
fun save do
- sys.stdout.write "Linking nodes...{save_cursor} "
+ sys.stdout.write "Linking nodes...{term_save_cursor} "
model.put_edges
- print "{reset_line} Done."
+ print "{term_rewind} Done."
var nodes = model.all_nodes
- sys.stdout.write "Saving {nodes.length} nodes...{save_cursor} "
- push_all(nodes)
+ sys.stdout.write "Saving {nodes.length} nodes..."
+ store.save_all(nodes)
var edges = model.all_edges
- sys.stdout.write "Saving {edges.length} edges...{save_cursor} "
- push_all(edges)
- end
-
- # Save `neo_entities` in the database using batch mode.
- private fun push_all(neo_entities: Collection[NeoEntity]) do
- var batch = new NeoBatch(client)
- var len = neo_entities.length
- var sum = 0
- var i = 1
-
- for nentity in neo_entities do
- batch.save_entity(nentity)
- if i == batch_max_size then
- do_batch(batch)
- sum += batch_max_size
- sys.stdout.write("{reset_line} {sum * 100 / len}% ")
- batch = new NeoBatch(client)
- i = 1
- else
- i += 1
- end
- end
- do_batch(batch)
- print("{reset_line} Done.")
- end
-
- # Execute `batch` and check for errors.
- #
- # Abort if `batch.execute` returns errors.
- private fun do_batch(batch: NeoBatch) do
- var errors = batch.execute
- if not errors.is_empty then
- for e in errors do sys.stderr.write("{sys.program_name}: {e}\n")
- exit(1)
- end
+ sys.stdout.write "Saving {edges.length} edges..."
+ store.save_all(edges)
end
end
init do
sources["any"] = new DefaultSource
sources["java"] = new JavaSource
+ sources["python"] = new PythonSource
var prefix = new OptionText("""
{{{"NAME".bold}}}
var keys = new Array[String].from(sources.keys)
opt_src_lang = new OptionEnum(keys,
- "The programming language to assume when processing chunk in the declarations left as-is by Doxygen. Use `any` (the default) to disable any language-specific processing.",
+ "The programming language to assume when processing chunks in the declarations left as-is by Doxygen. Use `any` (the default) to disable any language-specific processing.",
keys.index_of("any"), "--src-lang")
option_context.add_option(opt_src_lang)
end
var dest = opt_dest.value
var project_name = rest[0]
var dir = rest[1]
- var neo = new NeoDoxygenJob(new Neo4jClient(dest or else default_dest))
+ var neo = new NeoDoxygenJob(create_store(dest or else default_dest))
neo.load_project(project_name, dir, source)
neo.save
return 0
end
+ # Create an instance of `GraphStore` for the specified destination.
+ protected fun create_store(dest: String): GraphStore do
+ return new Neo4jStore(new Neo4jClient(dest))
+ end
+
# Show the help.
fun show_help do
option_context.usage
# Add handling of multi-line descriptions.
#
-# Note: The algorithm is naive and do not handle internationalisation and
-# escape sequences.
+# Note: The algorithm is naive and do not handle internationalisation,
+# multi-byte characters and control characters.
redef class Option
redef fun pretty(off) do