Merge: markdown: merge MDProcessor and MDEmitter
authorJean Privat <jean@pryen.org>
Tue, 17 Oct 2017 20:09:25 +0000 (16:09 -0400)
committerJean Privat <jean@pryen.org>
Tue, 17 Oct 2017 20:09:25 +0000 (16:09 -0400)
The emitter was unecessary.

Also done some cleaning.

Pull-Request: #2563

13 files changed:
lib/config.nit
lib/markdown/markdown.nit
lib/nlp/README.md
lib/nlp/nlp.nit
lib/nlp/vsm.nit [deleted file]
lib/vsm/README.md [new file with mode: 0644]
lib/vsm/examples/example_vsm.nit [new file with mode: 0644]
lib/vsm/vsm.nit [new file with mode: 0644]
src/model/model.nit
src/model/model_collect.nit
src/modelize/modelize_property.nit
src/web/api_model.nit
tests/sav/example_vsm.res [new file with mode: 0644]

index 83566a6..eb2bdba 100644 (file)
@@ -93,7 +93,7 @@
 #
 # The `IniConfig` class extends `Config` to add an easy way to link your
 # configuration to an ini file.
-
+#
 # ~~~
 # class MyIniConfig
 #      super IniConfig
index 1a84b0e..b29372f 100644 (file)
@@ -1120,6 +1120,12 @@ class MDBlock
                        text.append "\n"
                        line = line.next
                end
+               var block = first_block
+               while block != null do
+                       text.append block.text
+                       text.append "\n"
+                       block = block.next
+               end
                return text.write_to_string
        end
 end
index c7196c4..9c718a9 100644 (file)
@@ -61,14 +61,6 @@ For ease of use, this wrapper introduce a Nit model to handle CoreNLP XML result
 [[doc: nlp::NLPProcessor::process_file]]
 [[doc: nlp::NLPProcessor::process_files]]
 
-## Vector Space Model
-
-[[doc: NLPVector]]
-
-[[doc: vector]]
-
-[[doc: nlp::NLPVector::cosine_similarity]]
-
 ## NitNLP binary
 
 The `nitnlp` binary is given as an example of NitNLP client.
index 4dd7cc9..0e4dba3 100644 (file)
@@ -23,17 +23,17 @@ import vsm
 redef class NLPDocument
 
        # `NLPVector` representing `self`.
-       var vector: NLPVector is lazy do
-               var vector = new NLPVector
+       var vector: Vector is lazy do
+               var vector = new Vector
                for sentence in sentences do
                        for token in sentence.tokens do
                                if not keep_pos_token(token) then continue
                                var lemma = token.lemma
                                if lemma_black_list.has(lemma) then continue
                                if not vector.has_key(lemma) then
-                                       vector[lemma] = 1
+                                       vector[lemma] = 1.0
                                else
-                                       vector[lemma] += 1
+                                       vector[lemma] += 1.0
                                end
                        end
                end
diff --git a/lib/nlp/vsm.nit b/lib/nlp/vsm.nit
deleted file mode 100644 (file)
index 7fe0a84..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# NLPVector Space Model.
-#
-# The Vector Space Model (VSM) is used to compare natural language texts.
-# Texts are translated to multidimensionnal vectors then compared by cosine
-# similarity.
-module vsm
-
-import counter
-
-# A multi-dimensional vector.
-class NLPVector
-       super Counter[String]
-
-       # Cosine similarity of `self` and `other`.
-       #
-       # Gives the proximity in the range `[0.0 .. 1.0]` where 0.0 means that the
-       # two vectors are orthogonal and 1.0 means that they are identical.
-       #
-       # ~~~
-       # var v1 = new NLPVector
-       # v1["x"] = 1
-       # v1["y"] = 2
-       # v1["z"] = 3
-       #
-       # var v2 = new NLPVector
-       # v2["x"] = 1
-       # v2["y"] = 2
-       # v2["z"] = 3
-       #
-       # var v3 = new NLPVector
-       # v3["a"] = 1
-       # v3["b"] = 2
-       # v3["c"] = 3
-       #
-       # print v1.cosine_similarity(v2)
-       # #assert v1.cosine_similarity(v2) == 1.0
-       # print v1.cosine_similarity(v3)
-       # assert v1.cosine_similarity(v3) == 0.0
-       # ~~~
-       fun cosine_similarity(other: SELF): Float do
-               # Collect terms
-               var terms = new HashSet[String]
-               for k in self.keys do terms.add k
-               for k in other.keys do terms.add k
-
-               # Get dot product of two verctors
-               var dot = 0
-               for term in terms do
-                       dot += self.get_or_default(term, 0) * other.get_or_default(term, 0)
-               end
-
-               return dot.to_f / (self.norm * other.norm)
-       end
-
-       # The norm of the vector.
-       #
-       # `||x|| = (x1 ** 2 ... + xn ** 2).sqrt`
-       #
-       # ~~~
-       # var v = new NLPVector
-       # v["x"] = 1
-       # v["y"] = 1
-       # v["z"] = 1
-       # v["t"] = 1
-       # assert v.norm.is_approx(2.0, 0.001)
-       #
-       # v["x"] = 1
-       # v["y"] = 2
-       # v["z"] = 3
-       # v["t"] = 0
-       # assert v.norm.is_approx(3.742, 0.001)
-       # ~~~
-       fun norm: Float do
-               var sum = 0
-               for v in self.values do sum += v ** 2
-               return sum.to_f.sqrt
-       end
-end
diff --git a/lib/vsm/README.md b/lib/vsm/README.md
new file mode 100644 (file)
index 0000000..ea50132
--- /dev/null
@@ -0,0 +1,103 @@
+# Vector Space Model
+
+Vector Space Model (VSM) is an algebraic model for representing text documents
+(and any objects, in general) as vectors of identifiers, such as, for example,
+index terms.
+
+It is used in information filtering, information retrieval, indexing and
+relevancy rankings.
+
+The `vsm` package provides the following features:
+* Vector comparison with cosine similarity.
+* Vector indexing and matching with tf * idf.
+* File indexing and matching to free text queries.
+
+## Vectors
+
+With VSM, documents are represented by a n-dimensions vector.
+Each dimension represent an attribute of the document or object.
+
+For text document, the count of each term found in the document if often used to
+build vectors.
+
+### Creating a vector
+
+~~~
+var vector = new Vector
+vector["term1"] = 2.0
+vector["term2"] = 1.0
+assert vector["term1"] == 2.0
+assert vector["term2"] == 1.0
+assert vector.norm.is_approx(2.236, 0.001)
+~~~
+
+### Comparing vectors
+
+~~~
+var v1 = new Vector
+v1["term1"] = 1.0
+v1["term2"] = 2.0
+
+var v2 = new Vector
+v2["term2"] = 1.0
+v2["term3"] = 3.0
+
+var query = new Vector
+query["term2"] = 1.0
+
+var s1 = query.cosine_similarity(v1)
+var s2 = query.cosine_similarity(v2)
+assert s1 > s2
+~~~
+
+## VSMIndex
+
+VSMIndex is a Document index based on VSM.
+
+Using VSMIndex you can index documents associated with their vector.
+Documents can then be matched to query vectors.
+
+This represents a minimalistic search engine.
+
+~~~
+var index = new VSMIndex
+
+var d1 = new Document("Doc 1", "/uri/1", v1)
+index.index_document(d1)
+
+var d2 = new Document("Doc 2", "/uri/2", v2)
+index.index_document(d2)
+
+assert index.documents.length == 2
+
+query = new Vector
+query["term1"] = 1.0
+
+var matches = index.match_vector(query)
+assert matches.first.document == d1
+~~~
+
+## StringIndex
+
+The StringIndex provides usefull services to index and match strings.
+
+~~~
+index = new StringIndex
+
+d1 = index.index_string("Doc 1", "/uri/1", "this is a sample")
+d2 = index.index_string("Doc 2", "/uri/2", "this and this is another example")
+assert index.documents.length == 2
+
+matches = index.match_string("this sample")
+assert matches.first.document == d1
+~~~
+
+## FileIndex
+
+The FileIndex is a StringIndex able to index and retrieve files.
+
+~~~nit
+index = new FileIndex
+
+index.index_files(["/path/to/doc/1", "/path/to/doc/2"])
+~~~
diff --git a/lib/vsm/examples/example_vsm.nit b/lib/vsm/examples/example_vsm.nit
new file mode 100644 (file)
index 0000000..5f1192b
--- /dev/null
@@ -0,0 +1,65 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Example using a `FileIndex`
+#
+# This example shows of to index files from the system and retrieve them
+# with text queries.
+module example_vsm
+
+import vsm
+import config
+
+redef class Config
+
+       # --whitelist-exts
+       var opt_white_exts = new OptionArray("Allowed file extensions (default is [])",
+               "-w", "--whitelist-exts")
+
+       # --blacklist-exts
+       var opt_black_exts = new OptionArray("Allowed file extensions (default is [])",
+               "-b", "--blacklist-exts")
+
+       redef init do
+               opts.add_option(opt_white_exts, opt_black_exts)
+       end
+end
+
+var config = new Config
+config.tool_description = "usage: example_vsm <files>"
+config.parse_options(args)
+
+if args.length < 1 then
+       config.usage
+       exit 1
+end
+
+var index = new FileIndex
+index.whitelist_exts = config.opt_white_exts.value
+index.blacklist_exts = config.opt_black_exts.value
+
+print "Building index..."
+index.index_files(args, true)
+print "Indexed {index.documents.length} documents"
+
+loop
+       print "\nEnter query:"
+       printn "> "
+       var input = sys.stdin.read_line
+       var matches = index.match_string(input)
+       printn ""
+       for match in matches do
+               print match
+       end
+end
diff --git a/lib/vsm/vsm.nit b/lib/vsm/vsm.nit
new file mode 100644 (file)
index 0000000..41d5152
--- /dev/null
@@ -0,0 +1,374 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Vector Space Model
+#
+# Vector Space Model (VSM) is an algebraic model for representing text documents
+# (and any objects, in general) as vectors of identifiers, such as, for example,
+# index terms.
+#
+# It is used in information filtering, information retrieval, indexing and
+# relevancy rankings.
+module vsm
+
+import counter
+
+# A n-dimensions vector
+#
+# *n-dimensions* vectors are used to represent a text document or an object.
+class Vector
+       super HashMap[nullable Object, Float]
+
+       # Cosine similarity of `self` and `other`.
+       #
+       # Gives the proximity in the range `[0.0 .. 1.0]` where 0.0 means that the
+       # two vectors are orthogonal and 1.0 means that they are identical.
+       #
+       # ~~~
+       # var v1 = new Vector
+       # v1["x"] = 1.0
+       # v1["y"] = 2.0
+       # v1["z"] = 3.0
+       #
+       # var v2 = new Vector
+       # v2["x"] = 1.0
+       # v2["y"] = 2.0
+       # v2["z"] = 3.0
+       #
+       # var v3 = new Vector
+       # v3["a"] = 1.0
+       # v3["b"] = 2.0
+       # v3["c"] = 3.0
+       #
+       # print v1.cosine_similarity(v2)
+       # assert v1.cosine_similarity(v2) == 1.0
+       # print v1.cosine_similarity(v3)
+       # assert v1.cosine_similarity(v3) == 0.0
+       # ~~~
+       fun cosine_similarity(other: SELF): Float do
+               # Collect terms
+               var terms = new HashSet[nullable Object]
+               for k in self.keys do terms.add k
+               for k in other.keys do terms.add k
+
+               # Get dot product of two vectors
+               var dot = 0.0
+               for term in terms do
+                       dot += self.get_or_default(term, 0.0) * other.get_or_default(term, 0.0)
+               end
+               var cos = dot.to_f / (self.norm * other.norm)
+               if cos.is_nan then return 0.0
+               return cos
+       end
+
+       # The norm of the vector.
+       #
+       # `||x|| = (x1 ** 2 ... + xn ** 2).sqrt`
+       #
+       # ~~~
+       # var v = new Vector
+       # v["x"] = 1.0
+       # v["y"] = 1.0
+       # v["z"] = 1.0
+       # v["t"] = 1.0
+       # assert v.norm.is_approx(2.0, 0.001)
+       #
+       # v["x"] = 1.0
+       # v["y"] = 2.0
+       # v["z"] = 3.0
+       # v["t"] = 0.0
+       # assert v.norm.is_approx(3.742, 0.001)
+       # ~~~
+       fun norm: Float do
+               var sum = 0.0
+               for v in self.values do sum += v.pow(2.0)
+               return sum.to_f.sqrt
+       end
+
+       redef fun to_s do
+               return "[{join(", ", ":")}]"
+       end
+end
+
+# A Document index based on VSM
+#
+# Using VSMIndex you can index documents associated with their vector.
+# Documents can then be matched to query vectors.
+class VSMIndex
+
+       # Documents index
+       #
+       # TODO use a more efficient representation.
+       var documents = new HashSet[Document]
+
+       # Count for all terms in all indexed documents
+       #
+       # Used to compute the `inverse_doc_frequency`.
+       var terms_doc_count = new Vector
+
+       # Inverse document frequency
+       #
+       # The inverse document frequency is a measure of how much information a term
+       # provides, that is, whether the term is common or rare across all documents.
+       var inverse_doc_frequency = new Vector
+
+       # Used to sort matches
+       #
+       # See `IndexMatch`.
+       var sorter = new IndexMatchSorter
+
+       # Match `query` vector to all index document vectors
+       #
+       # Returns an `IndexMatch` for each indexed document.
+       # Results are ordered by descending similarity.
+       fun match_vector(query: Vector): Array[IndexMatch] do
+               var matches = new Array[IndexMatch]
+               for doc in documents do
+                       var sim = query.cosine_similarity(doc.tfidf)
+                       if sim == 0.0 then continue
+                       matches.add new IndexMatch(doc, sim)
+               end
+               sorter.sort(matches)
+               return matches
+       end
+
+       # Index a document
+       #
+       # With each new document, the `inverse_doc_frequency` must be updated.
+       # By default, the method `update_index` is called after each call to
+       # `index_document`.
+       #
+       # When processing batch documents, use `auto_update = false` to disable
+       # the auto update of the index.
+       fun index_document(doc: Document, auto_update: nullable Bool) do
+               for term, count in doc.terms_count do
+                       if not terms_doc_count.has_key(term) then
+                               terms_doc_count[term] = 1.0
+                       else
+                               terms_doc_count[term] += 1.0
+                       end
+               end
+               documents.add doc
+               if auto_update == null or auto_update then update_index
+       end
+
+       # Update the index
+       #
+       # Recompute the `inverse_doc_frequency` values.
+       # Must be called manually after indexing new document with the option
+       # `auto_update = false`.
+       fun update_index do
+               for doc in documents do
+                       for term, ccount in doc.terms_count do
+                               inverse_doc_frequency[term] = (documents.length.to_f / terms_doc_count[term]).log
+                       end
+               end
+               for doc in documents do
+                       for term, freq in doc.terms_frequency do
+                               doc.tfidf[term] = freq * inverse_doc_frequency[term]
+                       end
+               end
+       end
+end
+
+# A VSM index to store strings
+class StringIndex
+       super VSMIndex
+
+       # Index a new Document from `title`, `uri` and string `string`.
+       #
+       # Return the Document created.
+       #
+       # See `index_document`.
+       fun index_string(title, uri, string: String, auto_update: nullable Bool): Document do
+               var vector = parse_string(string)
+               var doc = new Document(title, uri, vector)
+               index_document(doc, auto_update)
+               return doc
+       end
+
+       # Match the `query` string against all indexed documents
+       #
+       # See `match_vector`.
+       fun match_string(query: String): Array[IndexMatch] do
+               var vector = parse_string(query)
+               return match_vector(vector)
+       end
+
+       # Parse the `string` as a Vector
+       #
+       # Returns a vector containing the terms of `string`.
+       fun parse_string(string: String): Vector do
+               var reader = new StringReader(string)
+               var vector = new Vector
+               loop
+                       var token = reader.read_word
+                       if token == "" then break
+
+                       if not vector.has_key(token) then
+                               vector[token] = 1.0
+                       else
+                               vector[token] += 1.0
+                       end
+               end
+               return vector
+       end
+end
+
+# A VSMIndex to index files
+class FileIndex
+       super StringIndex
+
+       # Index a file from its `path`.
+       #
+       # Return the created document or null if `path` is not accepted by `accept_file`.
+       #
+       # See `index_document`.
+       fun index_file(path: String, auto_update: nullable Bool): nullable Document do
+               if not accept_file(path) then return null
+               var vector = parse_file(path)
+               var doc = new Document(path, path, vector)
+               index_document(doc, auto_update)
+               return doc
+       end
+
+       # Index multiple files
+       #
+       # The recursive method `index_dir` will be called for each directory found
+       # in `paths`.
+       #
+       # See `index_file`
+       fun index_files(paths: Collection[String], auto_update: nullable Bool) do
+               for path in paths do
+                       if path.to_path.is_dir then
+                               index_dir(path, false)
+                       else
+                               index_file(path, false)
+                       end
+               end
+               if auto_update != null and auto_update then update_index
+       end
+
+       # Index all files in `dir` recursively
+       #
+       # See `index_file`.
+       fun index_dir(dir: String, auto_update: nullable Bool) do
+               if not dir.to_path.is_dir then return
+               for file in dir.files do
+                       var path = dir / file
+                       if path.to_path.is_dir then
+                               index_dir(path, false)
+                       else
+                               index_file(path, false)
+                       end
+               end
+               if auto_update != null and auto_update then update_index
+       end
+
+       # Is `path` accepted depending on `whitelist_exts` and `blacklist_exts`?
+       fun accept_file(path: String): Bool do
+               var ext = path.file_extension
+               if ext != null then
+                       ext = ext.to_lower
+                       if blacklist_exts.has(ext) then return false
+                       if whitelist_exts.not_empty and not whitelist_exts.has(ext) then return false
+               end
+               return whitelist_exts.is_empty
+       end
+
+       # Parse the `file` content as a Vector
+       #
+       # See `parse_string`.
+       fun parse_file(file: String): Vector do
+               return parse_string(file.to_path.read_all)
+       end
+
+       # File extensions white list
+       #
+       # If not empty, only files with these extensions will be indexed.
+       #
+       # If an extension is in both `whitelist_exts` and `blacklist_exts`, the
+       # blacklist will prevail and the file will be ignored.
+       var whitelist_exts = new Array[String] is writable
+
+       # File extensions black list
+       #
+       # Files with these extensions will not be indexed.
+       var blacklist_exts = new Array[String] is writable
+end
+
+# A Document to add in a VSMIndex
+class Document
+
+       # Document title
+       var title: String
+
+       # Document URI
+       var uri: String
+
+       # Count of all terms found in the document
+       #
+       # Used to compute the document `terms_frequency`.
+       var terms_count: Vector
+
+       # Frequency of each term found in the document
+       #
+       # Used to match the document against the `VSMIndex::inverse_doc_frequency`.
+       var terms_frequency: Vector is lazy do
+               var all_terms = 0.0
+               for t, c in terms_count do all_terms += c
+
+               var vector = new Vector
+               for t, c in terms_count do
+                       vector[t] = c / all_terms
+               end
+               return vector
+       end
+
+       # Term frequency–Inverse document frequency for each term
+       #
+       # A high weight in tf–idf is reached by a high term frequency
+       # (in the given document) and a low document frequency of the term in the
+       # whole collection of documents
+       var tfidf = new Vector
+
+       redef fun to_s do return "{title}"
+end
+
+# A match to a `request` in an `Index`
+class IndexMatch
+       super Comparable
+
+       # Document matching the `request_vector`
+       var document: Document
+
+       # Similarity between the `request` and the `doc`.
+       #
+       # Result is in the range 0.0 .. 1.1 where 0.0 means no similarity and 1.0
+       # means perfect similarity.
+       var similarity: Float
+
+       redef fun to_s do return "{document} ({similarity})"
+end
+
+# Sort matches by similarity
+class IndexMatchSorter
+       super DefaultComparator
+
+       redef type COMPARED: IndexMatch
+
+       redef fun compare(a, b) do
+               return b.similarity <=> a.similarity
+       end
+end
index 47900c5..dc9b978 100644 (file)
@@ -2377,6 +2377,29 @@ class MMethod
        # A specific method that is safe to call on null.
        # Currently, only `==`, `!=` and `is_same_instance` are safe
        fun is_null_safe: Bool do return name == "==" or name == "!=" or name == "is_same_instance"
+
+       # Is this method a getter (auto or not)?
+       #
+       # See `getter_for`.
+       fun is_getter: Bool do return getter_for != null
+
+       # The attribute this getter is for
+       #
+       # Return `null` is this method is not a getter.
+       var getter_for: nullable MAttribute = null is writable
+
+       # Is this method a setter (auto or not)?
+       #
+       # See `setter_for`.
+       fun is_setter: Bool do return setter_for != null
+
+       # The attribute this setter is for
+       #
+       # Return `null` is this method is not a setter.
+       var setter_for: nullable MAttribute = null is writable
+
+       # Is this method a getter or a setter?
+       fun is_accessor: Bool do return is_getter or is_setter
 end
 
 # A global attribute
@@ -2385,6 +2408,21 @@ class MAttribute
 
        redef type MPROPDEF: MAttributeDef
 
+       # Does this attribute have a getter (auto or not)?
+       #
+       # See `getter`.
+       fun has_getter: Bool do return getter != null
+
+       # The getter of this attribute (if any)
+       var getter: nullable MProperty = null is writable
+
+       # Does this attribute have a setter (auto or not)?
+       #
+       # See `setter`.
+       fun has_setter: Bool do return setter != null
+
+       # The setter of this attribute (if any)
+       var setter: nullable MProperty = null is writable
 end
 
 # A global virtual type
@@ -2420,7 +2458,7 @@ abstract class MPropDef
        # The associated global property
        var mproperty: MPROPERTY
 
-       redef var location: Location
+       redef var location
 
        redef fun visibility do return mproperty.visibility
 
index bfe5fdf..627f982 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Collect things from a `Model`.
+# Collect things from a `ModelView`
+#
+# This module introduce several usefull methods to list and count things from a
+# ModelView.
+#
+# First setup you view from a Model:
+#
+# ~~~nitih
+# var view = new ModelView(model)
+# ~~~
+#
+# Then ask question using the view:
+#
+# ~~~nitish
+# print number of parents for `{my_class}`
+# print my_class.collect_parents(view).count
+# ~~~
 #
 # **Warning**
 #
 # `model_collect` offers a flattened view of the model without considering any
 # main module.
 # For this reason, `model_collect` lists all the definitions reachable from all
-# modules
+# modules.
 #
 # This is usefull for tools that need a global view of a model like `nitdoc`,
-# `nitx` or `nituml`.
+# `nitx`, `nitmetrics` or `nituml`.
 # It should not be used for compiling stuffs like computing VFT, where the listed
 # entities could not be reachable depending on the modules really imported.
 module model_collect
@@ -33,20 +49,18 @@ import model_views
 
 redef class MEntity
 
-       # FIXME used to bypass RTA limitation on type resolution.
+       # FIXME used to bypass RTA limitation on type resolution
        type MENTITY: SELF
 
-       # Collect modifier keywords like `redef`, `private` etc.
-       fun collect_modifiers: Array[String] do
-               return new Array[String]
-       end
+       # Collect modifier keywords like `redef`, `private` etc
+       fun collect_modifiers: Array[String] do return new Array[String]
 
-       # Collect `self` linearization anchored on `mainmodule`.
+       # Collect `self` linearization anchored on `mainmodule`
        fun collect_linearization(mainmodule: MModule): nullable Array[MEntity] do
                return null
        end
 
-       # Collect `self` ancestors (direct and indirect).
+       # Collect `self` ancestors (direct and indirect)
        #
        # The concept of ancestor is abstract at this stage.
        fun collect_ancestors(view: ModelView): Set[MENTITY] do
@@ -57,24 +71,23 @@ redef class MEntity
                while todo.not_empty do
                        var mentity = todo.pop
                        if mentity == self or done.has(mentity) then continue
-                       print "{mentity} == {self}"
                        done.add mentity
                        todo.add_all mentity.collect_parents(view)
                end
                return done
        end
 
-       # Collect `self` parents (direct ancestors).
+       # Collect `self` parents (direct ancestors)
        #
        # The concept of parent is abstract at this stage.
        fun collect_parents(view: ModelView): Set[MENTITY] is abstract
 
-       # Collect `self` children (direct descendants).
+       # Collect `self` children (direct descendants)
        #
        # The concept of child is abstract at this stage.
        fun collect_children(view: ModelView): Set[MENTITY] is abstract
 
-       # Collect `self` descendants (direct and direct).
+       # Collect `self` descendants (direct and direct)
        #
        # The concept of descendant is abstract at this stage.
        fun collect_descendants(view: ModelView): Set[MENTITY] do
@@ -91,18 +104,18 @@ redef class MEntity
                return done
        end
 
-       # Build a poset representing `self` in it's own hierarchy.
+       # Build a poset representing `self` in it's own hierarchy
        #
        # The notion of hierarchy depends on the type of MEntity.
        #
        # Here a recap:
-       # * MPackage: package dependencies
-       # * MGroup: group dependencies
-       # * MModule: modules imports
-       # * MClass: class inheritance (all classdefs flattened)
-       # * MClassDef: classdef inheritance
-       # * MProperty: property definitions graph (all propdefs flattened)
-       # * MPropDef: property definitions graph
+       # * `MPackage`: package dependencies
+       # * `MGroup`: group dependencies
+       # * `MModule`: modules imports
+       # * `MClass`: class inheritance (all classdefs flattened)
+       # * `MClassDef`: classdef inheritance
+       # * `MProperty`: property definitions graph (all propdefs flattened)
+       # * `MPropDef`: property definitions graph
        fun hierarchy_poset(view: ModelView): POSet[MENTITY] do
                var done = new HashSet[MENTITY]
                var mentities = new Array[MENTITY]
@@ -126,14 +139,51 @@ redef class MEntity
        end
 end
 
-redef class MPackage
-       redef fun collect_modifiers do
-               var res = super
-               res.add "package"
+redef class Model
+
+       # Collect all MPackages in `self`
+       fun collect_mpackages(view: ModelView): HashSet[MPackage] do
+               var res = new HashSet[MPackage]
+               for mpackage in mpackages do
+                       if not view.accept_mentity(mpackage) then continue
+                       res.add(mpackage)
+               end
+               return res
+       end
+
+       # Collect all MModules in `self`
+       fun collect_mmodules(view: ModelView): HashSet[MModule] do
+               var res = new HashSet[MModule]
+               for mpackage in collect_mpackages(view) do
+                       res.add_all mpackage.collect_all_mmodules(view)
+               end
+               return res
+       end
+
+       # Collect all MClasses introduced in `self`
+       fun collect_intro_mclasses(view: ModelView): HashSet[MClass] do
+               var res = new HashSet[MClass]
+               for mpackage in collect_mpackages(view) do
+                       res.add_all mpackage.collect_intro_mclasses(view)
+               end
+               return res
+       end
+
+       # Collect all MProperties introduced in `self`
+       fun collect_intro_mproperties(view: ModelView): HashSet[MProperty] do
+               var res = new HashSet[MProperty]
+               for mpackage in collect_mpackages(view) do
+                       res.add_all mpackage.collect_intro_mproperties(view)
+               end
                return res
        end
+end
+
+redef class MPackage
+
+       redef fun collect_modifiers do return super + ["package"]
 
-       # `MPackage` parents are its direct dependencies.
+       # Collect all packages directly imported by `self`
        redef fun collect_parents(view) do
                var res = new HashSet[MENTITY]
                for mgroup in mgroups do
@@ -146,7 +196,7 @@ redef class MPackage
                return res
        end
 
-       # `MPackage` children are packages that directly depends on `self`.
+       # Collect all packages that directly depends on `self`
        redef fun collect_children(view) do
                var res = new HashSet[MENTITY]
                for mpackage in view.mpackages do
@@ -155,27 +205,139 @@ redef class MPackage
                return res
        end
 
-       # `MModules` contained in `self`.
+       # Collect all groups contained in `self`
+       fun collect_all_mgroups(view: ModelView): HashSet[MGroup] do
+               var res = new HashSet[MGroup]
+               for mgroup in mgroups do
+                       if not view.accept_mentity(mgroup) then continue
+                       res.add(mgroup)
+               end
+               return res
+       end
+
+       # Collect only groups contained in `self.root`
+       fun collect_mgroups(view: ModelView): HashSet[MGroup] do
+               var res = new HashSet[MGroup]
+               var root = self.root
+               if root == null then return res
+               res.add_all root.collect_mgroups(view)
+               return res
+       end
+
+       # Collect all modules contained in `self`
+       fun collect_all_mmodules(view: ModelView): HashSet[MModule] do
+               var res = new HashSet[MModule]
+               for mgroup in collect_mgroups(view) do
+                       res.add_all mgroup.collect_mmodules(view)
+               end
+               return res
+       end
+
+       # Collect only modules contained in `self.root`
        fun collect_mmodules(view: ModelView): HashSet[MModule] do
                var res = new HashSet[MModule]
+               var root = self.root
+               if root == null then return res
+               res.add_all root.collect_mmodules(view)
+               return res
+       end
+
+       # Collect all classes introduced in `self`
+       fun collect_intro_mclasses(view: ModelView): HashSet[MClass] do
+               var res = new HashSet[MClass]
                for mgroup in mgroups do
-                       for mmodule in mgroup.mmodules do
-                               if not view.accept_mentity(mmodule) then continue
-                               res.add(mmodule)
+                       for mmodule in collect_all_mmodules(view) do
+                               res.add_all mmodule.collect_intro_mclasses(view)
                        end
                end
                return res
        end
-end
 
-redef class MGroup
-       redef fun collect_modifiers do
-               var res = super
-               res.add "group"
+       # Collect all classes redefined or refined in `self`
+       fun collect_redef_mclasses(view: ModelView): Set[MClass] do
+               var res = new HashSet[MClass]
+               for mgroup in mgroups do
+                       for mmodule in collect_all_mmodules(view) do
+                               res.add_all mmodule.collect_redef_mclasses(view)
+                       end
+               end
                return res
        end
 
-       # `MGroup` parents are its direct dependencies.
+       # Collect all properties introduced in `self`
+       fun collect_intro_mproperties(view: ModelView): HashSet[MProperty] do
+               var res = new HashSet[MProperty]
+               for mgroup in mgroups do
+                       for mmodule in collect_all_mmodules(view) do
+                               res.add_all mmodule.collect_intro_mproperties(view)
+                       end
+               end
+               return res
+       end
+
+       # Collect all properties redefined in `self`
+       fun collect_redef_mproperties(view: ModelView): HashSet[MProperty] do
+               var res = new HashSet[MProperty]
+               for mgroup in mgroups do
+                       for mmodule in collect_all_mmodules(view) do
+                               res.add_all mmodule.collect_redef_mproperties(view)
+                       end
+               end
+               return res
+       end
+
+       # Collect all attributes introduced in `self`
+       fun collect_intro_attributes(view: ModelView): Set[MAttribute] do
+               var res = new HashSet[MAttribute]
+               for mgroup in mgroups do
+                       for mmodule in collect_all_mmodules(view) do
+                               res.add_all mmodule.collect_intro_attributes(view)
+                       end
+               end
+               return res
+       end
+
+       # Collect all inits introduced in `self`
+       fun collect_intro_inits(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mgroup in mgroups do
+                       for mmodule in collect_all_mmodules(view) do
+                               res.add_all mmodule.collect_intro_inits(view)
+                       end
+               end
+               return res
+       end
+
+       # Collect all methods introduced in `self` excluding inits
+       #
+       # See `collect_intro_inits`.
+       fun collect_intro_methods(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mgroup in mgroups do
+                       for mmodule in collect_all_mmodules(view) do
+                               res.add_all mmodule.collect_intro_methods(view)
+                       end
+               end
+               return res
+       end
+
+       # Collect all virtual types introduced in `self`
+       fun collect_intro_vts(view: ModelView): Set[MVirtualTypeProp] do
+               var res = new HashSet[MVirtualTypeProp]
+               for mgroup in mgroups do
+                       for mmodule in collect_all_mmodules(view) do
+                               res.add_all mmodule.collect_intro_vts(view)
+                       end
+               end
+               return res
+       end
+end
+
+redef class MGroup
+
+       redef fun collect_modifiers do return super + ["group"]
+
+       # Collect all groups directly import by `self`
        redef fun collect_parents(view) do
                var res = new HashSet[MENTITY]
                for mmodule in mmodules do
@@ -189,7 +351,7 @@ redef class MGroup
                return res
        end
 
-       # `MGroup` children are mgroups that directly depends on `self`.
+       # Collect all group that directly import `self`
        redef fun collect_children(view) do
                var res = new HashSet[MENTITY]
                for mgroup in view.mgroups do
@@ -198,17 +360,33 @@ redef class MGroup
                end
                return res
        end
-end
 
-redef class MModule
+       # Collect all groups contained in `self`
+       fun collect_mgroups(view: ModelView): HashSet[MENTITY] do
+               var res = new HashSet[MENTITY]
+               for mgroup in in_nesting.direct_smallers do
+                       if not view.accept_mentity(mgroup) then continue
+                       res.add(mgroup)
+               end
+               return res
+       end
 
-       redef fun collect_modifiers do
-               var res = super
-               res.add "module"
+       # Collect all modules contained in `self`
+       fun collect_mmodules(view: ModelView): HashSet[MModule] do
+               var res = new HashSet[MModule]
+               for mmodule in mmodules do
+                       if not view.accept_mentity(mmodule) then continue
+                       res.add(mmodule)
+               end
                return res
        end
+end
+
+redef class MModule
 
-       # `MModule` ancestors are all its transitive imports.
+       redef fun collect_modifiers do return super + ["module"]
+
+       # Collect all module ancestors of `self` (direct and transitive imports)
        redef fun collect_ancestors(view) do
                var res = new HashSet[MENTITY]
                for mentity in in_importation.greaters do
@@ -219,7 +397,7 @@ redef class MModule
                return res
        end
 
-       # `MModule` parents are all its direct imports.
+       # Collect all modules directly imported by `self`
        redef fun collect_parents(view) do
                var res = new HashSet[MENTITY]
                for mentity in in_importation.direct_greaters do
@@ -230,7 +408,7 @@ redef class MModule
                return res
        end
 
-       # `MModule` children are modules that directly import `self`.
+       # Collect all modules that directly import `self`
        redef fun collect_children(view) do
                var res = new HashSet[MENTITY]
                for mentity in in_importation.direct_smallers do
@@ -241,7 +419,7 @@ redef class MModule
                return res
        end
 
-       # `MModule` children are modules that transitively import `self`.
+       # Collect all module descendants of `self` (direct and transitive imports)
        redef fun collect_descendants(view) do
                var res = new HashSet[MENTITY]
                for mentity in in_importation.smallers do
@@ -252,7 +430,7 @@ redef class MModule
                return res
        end
 
-       # Collect mclassdefs introduced in `self` with `visibility >= to min_visibility`.
+       # Collect all class definitions introduced in `self`
        fun collect_intro_mclassdefs(view: ModelView): Set[MClassDef] do
                var res = new HashSet[MClassDef]
                for mclassdef in mclassdefs do
@@ -263,7 +441,7 @@ redef class MModule
                return res
        end
 
-       # Collect mclassdefs redefined in `self` with `visibility >= to min_visibility`.
+       # Collect all class definitions refined in `self`
        fun collect_redef_mclassdefs(view: ModelView): Set[MClassDef] do
                var res = new HashSet[MClassDef]
                for mclassdef in mclassdefs do
@@ -274,7 +452,15 @@ redef class MModule
                return res
        end
 
-       # Collect mclasses introduced in `self` with `visibility >= to min_visibility`.
+       # Collect all class definitions introduced and refined in `self`
+       fun collect_local_mclassdefs(view: ModelView): Set[MClassDef] do
+               var res = new HashSet[MClassDef]
+               res.add_all collect_intro_mclassdefs(view)
+               res.add_all collect_redef_mclassdefs(view)
+               return res
+       end
+
+       # Collect all classes introduced in `self`
        fun collect_intro_mclasses(view: ModelView): Set[MClass] do
                var res = new HashSet[MClass]
                for mclass in intro_mclasses do
@@ -284,15 +470,90 @@ redef class MModule
                return res
        end
 
-       # Collect mclasses redefined in `self` with `visibility >= to min_visibility`.
+       # Collect all classes refined in `self`
        fun collect_redef_mclasses(view: ModelView): Set[MClass] do
                var mclasses = new HashSet[MClass]
                for mclassdef in mclassdefs do
-                       if not view.accept_mentity(mclassdef) then continue
+                       if not view.accept_mentity(mclassdef.mclass) then continue
                        if not mclassdef.is_intro then mclasses.add(mclassdef.mclass)
                end
                return mclasses
        end
+
+       # Collect all classes introduced and refined in `self`
+       fun collect_local_mclasses(view: ModelView): Set[MClass] do
+               var res = new HashSet[MClass]
+               res.add_all collect_intro_mclasses(view)
+               res.add_all collect_redef_mclasses(view)
+               return res
+       end
+
+       # Collect all classes imported from `self` parents
+       fun collect_imported_mclasses(view: ModelView): Set[MClass] do
+               var res = new HashSet[MClass]
+               for parent in collect_parents(view) do
+                       res.add_all parent.collect_intro_mclasses(view)
+                       res.add_all parent.collect_redef_mclasses(view)
+                       res.add_all parent.collect_imported_mclasses(view)
+               end
+               return res
+       end
+
+       # Collect all properties introduced in `self`
+       fun collect_intro_mproperties(view: ModelView): Set[MProperty] do
+               var res = new HashSet[MProperty]
+               for mclass in collect_intro_mclasses(view) do
+                       res.add_all mclass.collect_intro_mproperties(view)
+               end
+               return res
+       end
+
+       # Collect properties redefined in `self`
+       fun collect_redef_mproperties(view: ModelView): Set[MProperty] do
+               var res = new HashSet[MProperty]
+               for mclassdef in mclassdefs do
+                       for mpropdef in mclassdef.collect_redef_mpropdefs(view) do
+                               res.add mpropdef.mproperty
+                       end
+               end
+               return res
+       end
+
+       # Collect attributes introduced in `self`
+       fun collect_intro_attributes(view: ModelView): Set[MAttribute] do
+               var res = new HashSet[MAttribute]
+               for mproperty in collect_intro_mproperties(view) do
+                       if mproperty isa MAttribute then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect all inits introduced in `self`
+       fun collect_intro_inits(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mproperty in collect_intro_mproperties(view) do
+                       if mproperty isa MMethod and mproperty.is_init then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect methods introduced in `self` (without inits)
+       fun collect_intro_methods(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mproperty in collect_intro_mproperties(view) do
+                       if mproperty isa MMethod and not mproperty.is_init then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect virtual types introduced in `self`
+       fun collect_intro_vts(view: ModelView): Set[MVirtualTypeProp] do
+               var res = new HashSet[MVirtualTypeProp]
+               for mproperty in collect_intro_mproperties(view) do
+                       if mproperty isa MVirtualTypeProp then res.add(mproperty)
+               end
+               return res
+       end
 end
 
 redef class MClass
@@ -305,7 +566,7 @@ redef class MClass
                return mclassdefs
        end
 
-       # `MClass` parents are the direct parents of `self`.
+       # Collect all direct parents of `self`
        #
        # This method uses a flattened hierarchy containing all the mclassdefs.
        redef fun collect_parents(view) do
@@ -320,7 +581,7 @@ redef class MClass
                return res
        end
 
-       # Collect all ancestors of `self` with `visibility >= to min_visibility`.
+       # Collect all ancestors of `self`
        redef fun collect_ancestors(view) do
                var res = new HashSet[MENTITY]
                for mclassdef in mclassdefs do
@@ -332,7 +593,7 @@ redef class MClass
                return res
        end
 
-       # `MClass` parents are the direct parents of `self`.
+       # Collect all direct children of `self`
        #
        # This method uses a flattened hierarchy containing all the mclassdefs.
        redef fun collect_children(view) do
@@ -347,7 +608,30 @@ redef class MClass
                return res
        end
 
-       # Collect all mproperties introduced in 'self' with `visibility >= min_visibility`.
+       # Collect all class definitions of `self`
+       fun collect_mclassdefs(view: ModelView): Set[MClassDef] do
+               var res = new HashSet[MClassDef]
+               for mclassdef in mclassdefs do
+                       if not view.accept_mentity(mclassdef) then continue
+                       res.add mclassdef
+               end
+               return res
+       end
+
+       # Collect all property definitions that are introductions in `self`
+       fun collect_intro_mpropdefs(view: ModelView): Set[MPropDef] do
+               var set = new HashSet[MPropDef]
+               for mclassdef in mclassdefs do
+                       for mpropdef in mclassdef.mpropdefs do
+                               if not mpropdef.is_intro then continue
+                               if not view.accept_mentity(mpropdef) then continue
+                               set.add(mpropdef)
+                       end
+               end
+               return set
+       end
+
+       # Collect all properties introduced in `self`
        fun collect_intro_mproperties(view: ModelView): Set[MProperty] do
                var set = new HashSet[MProperty]
                for mclassdef in mclassdefs do
@@ -359,7 +643,20 @@ redef class MClass
                return set
        end
 
-       # Collect all mproperties redefined in 'self' with `visibility >= min_visibility`.
+       # Collect all propierty definitions that are redefinition in `self`
+       fun collect_redef_mpropdefs(view: ModelView): Set[MPropDef] do
+               var set = new HashSet[MPropDef]
+               for mclassdef in mclassdefs do
+                       for mpropdef in mclassdef.mpropdefs do
+                               if mpropdef.is_intro then continue
+                               if not view.accept_mentity(mpropdef) then continue
+                               set.add(mpropdef)
+                       end
+               end
+               return set
+       end
+
+       # Collect all properties redefined in `self`
        fun collect_redef_mproperties(view: ModelView): Set[MProperty] do
                var set = new HashSet[MProperty]
                for mclassdef in mclassdefs do
@@ -372,7 +669,7 @@ redef class MClass
                return set
        end
 
-       # Collect mproperties introduced and redefined in 'self' with `visibility >= min_visibility`.
+       # Collect all properties introduced and redefined in `self`
        fun collect_local_mproperties(view: ModelView): Set[MProperty] do
                var set = new HashSet[MProperty]
                set.add_all collect_intro_mproperties(view)
@@ -380,7 +677,7 @@ redef class MClass
                return set
        end
 
-       # Collect all mproperties inehrited by 'self' with `visibility >= min_visibility`.
+       # Collect all properties inehrited by `self`
        fun collect_inherited_mproperties(view: ModelView): Set[MProperty] do
                var set = new HashSet[MProperty]
                for parent in collect_parents(view) do
@@ -390,9 +687,9 @@ redef class MClass
                return set
        end
 
-       # Collect all mproperties accessible by 'self' with `visibility >= min_visibility`.
+       # Collect all properties accessible by `self`
        #
-       # This include introduced, redefined, inherited mproperties.
+       # This include introduced, redefined, inherited properties.
        fun collect_accessible_mproperties(view: ModelView): Set[MProperty] do
                var set = new HashSet[MProperty]
                set.add_all(collect_intro_mproperties(view))
@@ -401,7 +698,7 @@ redef class MClass
                return set
        end
 
-       # Collect mmethods introduced in 'self' with `visibility >= min_visibility`.
+       # Collect all methods introduced in `self`
        fun collect_intro_mmethods(view: ModelView): Set[MMethod] do
                var res = new HashSet[MMethod]
                for mproperty in collect_intro_mproperties(view) do
@@ -410,7 +707,7 @@ redef class MClass
                return res
        end
 
-       # Collect mmethods redefined in 'self' with `visibility >= min_visibility`.
+       # Collect all methods redefined in `self`
        fun collect_redef_mmethods(view: ModelView): Set[MMethod] do
                var res = new HashSet[MMethod]
                for mproperty in collect_redef_mproperties(view) do
@@ -419,7 +716,7 @@ redef class MClass
                return res
        end
 
-       # Collect mmethods introduced and redefined in 'self' with `visibility >= min_visibility`.
+       # Collect all methods introduced and redefined in `self`
        fun collect_local_mmethods(view: ModelView): Set[MMethod] do
                var set = new HashSet[MMethod]
                set.add_all collect_intro_mmethods(view)
@@ -427,7 +724,7 @@ redef class MClass
                return set
        end
 
-       # Collect mmethods inherited by 'self' if accepted by `view`.
+       # Collect all methods inherited by `self`
        fun collect_inherited_mmethods(view: ModelView): Set[MMethod] do
                var res = new HashSet[MMethod]
                for mproperty in collect_inherited_mproperties(view) do
@@ -436,9 +733,9 @@ redef class MClass
                return res
        end
 
-       # Collect all mmethods accessible by 'self' with `visibility >= min_visibility`.
+       # Collect all methods accessible by `self`
        #
-       # This include introduced, redefined, inherited mmethods.
+       # This include introduced, redefined, inherited methods.
        fun collect_accessible_mmethods(view: ModelView): Set[MMethod] do
                var set = new HashSet[MMethod]
                set.add_all(collect_intro_mmethods(view))
@@ -447,7 +744,7 @@ redef class MClass
                return set
        end
 
-       # Collect mattributes introduced in 'self' with `visibility >= min_visibility`.
+       # Collect all attributes introduced in `self`
        fun collect_intro_mattributes(view: ModelView): Set[MAttribute] do
                var res = new HashSet[MAttribute]
                for mproperty in collect_intro_mproperties(view) do
@@ -456,7 +753,7 @@ redef class MClass
                return res
        end
 
-       # Collect mattributes redefined in 'self' with `visibility >= min_visibility`.
+       # Collect all attributes redefined in `self`
        fun collect_redef_mattributes(view: ModelView): Set[MAttribute] do
                var res = new HashSet[MAttribute]
                for mproperty in collect_redef_mproperties(view) do
@@ -465,7 +762,7 @@ redef class MClass
                return res
        end
 
-       # Collect mattributes introduced and redefined in 'self' with `visibility >= min_visibility`.
+       # Collect all attributes introduced and redefined in `self`
        fun collect_local_mattributes(view: ModelView): Set[MAttribute] do
                var set = new HashSet[MAttribute]
                set.add_all collect_intro_mattributes(view)
@@ -473,7 +770,7 @@ redef class MClass
                return set
        end
 
-       # Collect mattributes inherited by 'self' with `visibility >= min_visibility`.
+       # Collect all attributes inherited by `self`
        fun collect_inherited_mattributes(view: ModelView): Set[MAttribute] do
                var res = new HashSet[MAttribute]
                for mproperty in collect_inherited_mproperties(view) do
@@ -482,7 +779,7 @@ redef class MClass
                return res
        end
 
-       # Collect all mattributes accessible by 'self' with `visibility >= min_visibility`.
+       # Collect all attributes accessible by `self`
        #
        # This include introduced, redefined, inherited mattributes.
        fun collect_accessible_mattributes(view: ModelView): Set[MAttribute] do
@@ -493,7 +790,7 @@ redef class MClass
                return set
        end
 
-       # Collect init mmethods introduced in 'self' if accepted by `view`.
+       # Collect all init methods introduced in `self`
        fun collect_intro_inits(view: ModelView): Set[MMethod] do
                var res = new HashSet[MMethod]
                for mproperty in collect_intro_mmethods(view) do
@@ -502,7 +799,7 @@ redef class MClass
                return res
        end
 
-       # Collect init mmethods redefined in 'self' if accepted by `view`.
+       # Collect all init methods redefined in `self`
        fun collect_redef_inits(view: ModelView): Set[MMethod] do
                var res = new HashSet[MMethod]
                for mproperty in collect_redef_mmethods(view) do
@@ -511,7 +808,7 @@ redef class MClass
                return res
        end
 
-       # Collect init mmethods introduced and redefined in 'self' if accepted by `view`.
+       # Collect all init methods introduced and redefined in `self`
        fun collect_local_inits(view: ModelView): Set[MMethod] do
                var set = new HashSet[MMethod]
                set.add_all collect_intro_inits(view)
@@ -519,7 +816,7 @@ redef class MClass
                return set
        end
 
-       # Collect init mmethods inherited by 'self'  if accepted by `view`.
+       # Collect all init methods inherited by `self`
        fun collect_inherited_inits(view: ModelView): Set[MMethod] do
                var res = new HashSet[MMethod]
                for mproperty in collect_inherited_mmethods(view) do
@@ -528,7 +825,7 @@ redef class MClass
                return res
        end
 
-       # Collect all init mmethods accessible by 'self'  if accepted by `view`.
+       # Collect all init methods accessible by `self`
        #
        # This include introduced, redefined, inherited inits.
        fun collect_accessible_inits(view: ModelView): Set[MMethod] do
@@ -539,7 +836,42 @@ redef class MClass
                return set
        end
 
-       # Collect all virtual types accessible by 'self'  if accepted by `view`.
+       # Collect all virtual types introduced in `self`
+       fun collect_intro_vts(view: ModelView): Set[MVirtualTypeProp] do
+               var res = new HashSet[MVirtualTypeProp]
+               for mproperty in collect_intro_mproperties(view) do
+                       if mproperty isa MVirtualTypeProp then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect all virtual types redefined in `self`
+       fun collect_redef_vts(view: ModelView): Set[MVirtualTypeProp] do
+               var res = new HashSet[MVirtualTypeProp]
+               for mproperty in collect_intro_mproperties(view) do
+                       if mproperty isa MVirtualTypeProp then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect all virtual types introduced or redefined in `self`
+       fun collect_local_vts(view: ModelView): Set[MVirtualTypeProp] do
+               var set = new HashSet[MVirtualTypeProp]
+               set.add_all collect_intro_vts(view)
+               set.add_all collect_redef_vts(view)
+               return set
+       end
+
+       # Collect all virtual types inherited by `self`
+       fun collect_inherited_vts(view: ModelView): Set[MVirtualTypeProp] do
+               var res = new HashSet[MVirtualTypeProp]
+               for mproperty in collect_inherited_mproperties(view) do
+                       if mproperty isa MVirtualTypeProp then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect all virtual types accessible by `self`
        #
        # This include introduced, redefined, inherited virtual types.
        fun collect_accessible_vts(view: ModelView): Set[MVirtualTypeProp] do
@@ -553,6 +885,17 @@ end
 
 redef class MClassDef
 
+       redef fun collect_modifiers do
+               var res = super
+               if not is_intro then
+                       res.add "redef"
+               else
+                       res.add mclass.visibility.to_s
+               end
+               res.add mclass.kind.to_s
+               return res
+       end
+
        redef fun collect_linearization(mainmodule) do
                var mclassdefs = new Array[MClassDef]
                for mclassdef in in_hierarchy.as(not null).greaters do
@@ -562,7 +905,6 @@ redef class MClassDef
                return mclassdefs
        end
 
-       # `MClassDef` ancestors are its direct and transitive super classes.
        redef fun collect_ancestors(view) do
                var res = new HashSet[MENTITY]
                var hierarchy = self.in_hierarchy
@@ -574,7 +916,6 @@ redef class MClassDef
                return res
        end
 
-       # `MClassDef` parents are its direct super classes.
        redef fun collect_parents(view) do
                var res = new HashSet[MENTITY]
                var hierarchy = self.in_hierarchy
@@ -586,7 +927,6 @@ redef class MClassDef
                return res
        end
 
-       # `MClassDef` children are its direct subclasses.
        redef fun collect_children(view) do
                var res = new HashSet[MENTITY]
                var hierarchy = self.in_hierarchy
@@ -598,7 +938,7 @@ redef class MClassDef
                return res
        end
 
-       # Collect mpropdefs in 'self' with `visibility >= min_visibility`.
+       # Collect all property definitions in `self`
        fun collect_mpropdefs(view: ModelView): Set[MPropDef] do
                var res = new HashSet[MPropDef]
                for mpropdef in mpropdefs do
@@ -608,7 +948,37 @@ redef class MClassDef
                return res
        end
 
-       # Collect mpropdefs introduced in 'self' with `visibility >= min_visibility`.
+       # Collect all attribute definitions in `self`
+       fun collect_mattributedefs(view: ModelView): Set[MAttributeDef] do
+               var res = new HashSet[MAttributeDef]
+               for mpropdef in collect_mpropdefs(view) do
+                       if not mpropdef isa MAttributeDef then continue
+                       res.add mpropdef
+               end
+               return res
+       end
+
+       # Collect all methods definitions in `self`
+       fun collect_mmethoddefs(view: ModelView): Set[MMethodDef] do
+               var res = new HashSet[MMethodDef]
+               for mpropdef in collect_mpropdefs(view) do
+                       if not mpropdef isa MMethodDef then continue
+                       res.add mpropdef
+               end
+               return res
+       end
+
+       # Collect all virtual types definitions in `self`
+       fun collect_mtypedefs(view: ModelView): Set[MVirtualTypeDef] do
+               var res = new HashSet[MVirtualTypeDef]
+               for mpropdef in collect_mpropdefs(view) do
+                       if not mpropdef isa MVirtualTypeDef then continue
+                       res.add mpropdef
+               end
+               return res
+       end
+
+       # Collect all property definitions that are introduction in `self`
        fun collect_intro_mpropdefs(view: ModelView): Set[MPropDef] do
                var res = new HashSet[MPropDef]
                for mpropdef in mpropdefs do
@@ -619,7 +989,7 @@ redef class MClassDef
                return res
        end
 
-       # Collect mpropdefs redefined in 'self' with `visibility >= min_visibility`.
+       # Collect all property definitions that are redefinition in `self`
        fun collect_redef_mpropdefs(view: ModelView): Set[MPropDef] do
                var res = new HashSet[MPropDef]
                for mpropdef in mpropdefs do
@@ -629,17 +999,6 @@ redef class MClassDef
                end
                return res
        end
-
-       redef fun collect_modifiers do
-               var res = super
-               if not is_intro then
-                       res.add "redef"
-               else
-                       res.add mclass.visibility.to_s
-               end
-               res.add mclass.kind.to_s
-               return res
-       end
 end
 
 redef class MProperty
@@ -651,7 +1010,7 @@ redef class MProperty
                return mpropdefs
        end
 
-       # Collect mpropdefs in 'self' with `visibility >= min_visibility`.
+       # Collect all property definitions of `self`
        fun collect_mpropdefs(view: ModelView): Set[MPropDef] do
                var res = new HashSet[MPropDef]
                for mpropdef in mpropdefs do
@@ -661,9 +1020,7 @@ redef class MProperty
                return res
        end
 
-       # `MProperty` parents are all direct super definition of `self`.
-       #
-       # This method uses a flattened hierarchy containing all the mpropdefs.
+       # Collect all direct super definitions of `self`
        redef fun collect_parents(view) do
                var res = new HashSet[MENTITY]
                for mpropdef in mpropdefs do
@@ -675,9 +1032,7 @@ redef class MProperty
                return res
        end
 
-       # `MProperty` parents are all direct sub definition of `self`.
-       #
-       # This method uses a flattened hierarchy containing all the mpropdefs.
+       # Collection all definitions that have `self` as a direct super definition
        redef fun collect_children(view) do
                var res = new HashSet[MENTITY]
                for mpropdef in mpropdefs do
@@ -691,6 +1046,7 @@ redef class MProperty
 end
 
 redef class MPropDef
+
        redef fun collect_modifiers do
                var res = super
                if not is_intro then
@@ -730,7 +1086,7 @@ redef class MPropDef
                return mpropdefs
        end
 
-       # `MPropDef` parents include only the next definition of `self`.
+       # Collect only the next definition of `self`
        redef fun collect_parents(view) do
                var res = new HashSet[MENTITY]
                var mpropdef = self
@@ -741,7 +1097,7 @@ redef class MPropDef
                return res
        end
 
-       # `MPropdef` children are definitions that directly depends on `self`.
+       # Collect all children definitions that directly depend on `self`
        redef fun collect_children(view) do
                var res = new HashSet[MENTITY]
                for mpropdef in mproperty.collect_mpropdefs(view) do
index 8880ef7..de23d04 100644 (file)
@@ -1206,6 +1206,12 @@ redef class AAttrPropdef
                end
                mclassdef.mprop2npropdef[mreadprop] = self
 
+               var attr_mpropdef = mpropdef
+               if attr_mpropdef != null then
+                       mreadprop.getter_for = attr_mpropdef.mproperty
+                       attr_mpropdef.mproperty.getter = mreadprop
+               end
+
                var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location)
                self.mreadpropdef = mreadpropdef
                modelbuilder.mpropdef2npropdef[mreadpropdef] = self
@@ -1312,6 +1318,11 @@ redef class AAttrPropdef
                end
                mclassdef.mprop2npropdef[mwriteprop] = self
 
+               if attr_mpropdef != null then
+                       mwriteprop.setter_for = attr_mpropdef.mproperty
+                       attr_mpropdef.mproperty.setter = mwriteprop
+               end
+
                var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location)
                self.mwritepropdef = mwritepropdef
                modelbuilder.mpropdef2npropdef[mwritepropdef] = self
index 328eff9..3d09a83 100644 (file)
@@ -216,21 +216,21 @@ class APIEntityDefs
        redef fun get(req, res) do
                var mentity = mentity_from_uri(req, res)
                if mentity == null then return
-               var mentities: Array[MEntity]
+               var mentities = new Array[MEntity]
                if mentity isa MPackage then
-                       mentities = mentity.mgroups.to_a
+                       mentities.add_all mentity.collect_mgroups(config.view)
+                       mentities.add_all mentity.collect_mmodules(config.view)
                else if mentity isa MGroup then
-                       mentities = new Array[MEntity]
-                       mentities.add_all mentity.in_nesting.direct_smallers
-                       mentities.add_all mentity.mmodules
+                       mentities.add_all mentity.collect_mgroups(config.view)
+                       mentities.add_all mentity.collect_mmodules(config.view)
                else if mentity isa MModule then
-                       mentities = mentity.mclassdefs
+                       mentities.add_all mentity.collect_local_mclassdefs(config.view)
                else if mentity isa MClass then
-                       mentities = mentity.mclassdefs
+                       mentities.add_all mentity.collect_mclassdefs(config.view)
                else if mentity isa MClassDef then
-                       mentities = mentity.mpropdefs
+                       mentities.add_all mentity.collect_mpropdefs(config.view)
                else if mentity isa MProperty then
-                       mentities = mentity.mpropdefs
+                       mentities.add_all mentity.collect_mpropdefs(config.view)
                else
                        res.api_error(404, "No definition list for mentity `{mentity.full_name}`")
                        return
diff --git a/tests/sav/example_vsm.res b/tests/sav/example_vsm.res
new file mode 100644 (file)
index 0000000..0e44311
--- /dev/null
@@ -0,0 +1,4 @@
+usage: example_vsm <files>
+  -h, --help             Show this help message
+  -w, --whitelist-exts   Allowed file extensions (default is [])
+  -b, --blacklist-exts   Allowed file extensions (default is [])