--- title: Collecting Items in Containers order: 5 layout: page --- [[datamodel.container]] = Collecting Items in Containers ((("[classname]#Container#", id="term.datamodel.container", range="startofrange"))) The [classname]#Container# interface is the highest containment level of the Vaadin data model, for containing items (rows) which in turn contain properties (columns). Containers can therefore represent tabular data, which can be viewed in a [classname]#Table# or some other selection component, as well as hierarchical data. The items contained in a container are identified by an __item identifier__ or __IID__, and the properties by a __property identifier__ or __PID__. [[datamodel.container.intro]] == Basic Use of Containers The basic use of containers involves creating one, adding items to it, and binding it as a container data source of a component. [[datamodel.container.intro.default]] === Default Containers and Delegation Before saying anything about creation of containers, it should be noted that all components that can be bound to a container data source are by default bound to a default container. For example, [classname]#Table# is bound to a [classname]#IndexedContainer#, [classname]#Tree# to a [classname]#HierarchicalContainer#, and so forth. All of the user interface components using containers also implement the relevant container interfaces themselves, so that the access to the underlying data source is delegated through the component. ---- // Create a table with one column Table table = new Table("My Table"); table.addContainerProperty("col1", String.class, null); // Access items and properties through the component table.addItem("row1"); // Create item by explicit ID Item item1 = table.getItem("row1"); Property property1 = item1.getItemProperty("col1"); property1.setValue("some given value"); // Equivalent access through the container Container container = table.getContainerDataSource(); container.addItem("row2"); Item item2 = container.getItem("row2"); Property property2 = item2.getItemProperty("col1"); property2.setValue("another given value"); ---- [[datamodel.container.intro.creating]] === Creating and Binding a Container A container is created and bound to a component as follows: ---- // Create a container of some type Container container = new IndexedContainer(); // Initialize the container as required by the container type container.addContainerProperty("name", String.class, "none"); container.addContainerProperty("volume", Double.class, 0.0); ... add items ... // Bind it to a component Table table = new Table("My Table"); table.setContainerDataSource(container); ---- Most components that can be bound to a container allow passing it also in the constructor, in addition to using [methodname]#setContainerDataSource()#. Creation of the container depends on its type. For some containers, such as the [classname]#IndexedContainer#, you need to define the contained properties (columns) as was done above, while some others determine them otherwise. The definition of a property with [methodname]#addContainerProperty()# requires a unique property ID, type, and a default value. You can also give [parameter]#null#. Vaadin has a several built-in in-memory container implementations, such as [classname]#IndexedContainer# and [classname]#BeanItemContainer#, which are easy to use for setting up nonpersistent data storages. For persistent data, either the built-in [classname]#SQLContainer# or the [classname]#JPAContainer# add-on container can be used. [[datamodel.container.intro.adding]] === Adding Items and Accessing Properties Items can be added to a container with the [methodname]#addItem()# method. The parameterless version of the method automatically generates the item ID. ---- // Create an item Object itemId = container.addItem(); ---- Properties can be requested from container by first requesting an item with [methodname]#getItem()# and then getting the properties from the item with [methodname]#getItemProperty()#. ---- // Get the item object Item item = container.getItem(itemId); // Access a property in the item Property nameProperty = item.getItemProperty("name"); // Do something with the property nameProperty.setValue("box"); ---- You can also get a property directly by the item and property ids with [methodname]#getContainerProperty()#. ---- container.getContainerProperty(itemId, "volume").setValue(5.0); ---- [[datamodel.container.intro.givenid]] === Adding Items by Given ID Some containers, such as [classname]#IndexedContainer# and [classname]#HierarchicalContainer#, allow adding items by a given ID, which can be any [classname]#Object#. ---- Item item = container.addItem("agivenid"); item.getItemProperty("name").setValue("barrel"); Item.getItemProperty("volume").setValue(119.2); ---- Notice that the actual item __is not__ given as a parameter to the method, only its ID, as the interface assumes that the container itself creates all the items it contains. Some container implementations can provide methods to add externally created items, and they can even assume that the item ID object is also the item itself. Lazy containers might not create the item immediately, but lazily when it is accessed by its ID. [[datamodel.container.inner]] == Container Subinterfaces The [classname]#Container# interface contains inner interfaces that container implementations can implement to fulfill different features required by components that present container data. [interfacename]#Container.Filterable#:: Filterable containers allow filtering the contained items by filters, as described in <>. [interfacename]#Container.Hierarchical#:: Hierarchical containers allow representing hierarchical relationships between items and are required by the [classname]#Tree# and [classname]#TreeTable# components. The [classname]#HierarchicalContainer# is a built-in in-memory container for hierarchical data, and is used as the default container for the tree components. The [classname]#FilesystemContainer# provides access to browsing the content of a file system. Also [classname]#JPAContainer# is hierarchical, as described in <>. [interfacename]#Container.Indexed#:: An indexed container allows accessing items by an index number, not just their item ID. This feature is required by some components, especially [classname]#Table#, which needs to provide lazy access to large containers. The [classname]#IndexedContainer# is a basic in-memory implementation, as described in <>. [interfacename]#Container.Ordered#:: An ordered container allows traversing the items in successive order in either direction. Most built-in containers are ordered. [interfacename]#Container.SimpleFilterable#:: This interface enables filtering a container by string matching with [methodname]#addContainerFilter()#. The filtering is done by either searching the given string anywhere in a property value, or as its prefix. [interfacename]#Container.Sortable#:: A sortable container is required by some components that allow sorting the content, such as [classname]#Table#, where the user can click a column header to sort the table by the column. Some other components, such as [classname]#Calendar#, may require that the content is sorted to be able to display it properly. Depending on the implementation, sorting can be done only when the [methodname]#sort()# method is called, or the container is automatically kept in order according to the last call of the method. See the API documentation for a detailed description of the interfaces. [[datamodel.container.indexedcontainer]] == [classname]#IndexedContainer# The [classname]#IndexedContainer# is an in-memory container that implements the [interfacename]#Indexed# interface to allow referencing the items by an index. [classname]#IndexedContainer# is used as the default container in most selection components in Vaadin. The properties need to be defined with [methodname]#addContainerProperty()#, which takes the property ID, type, and a default value. This must be done before any items are added to the container. ---- // Create the container IndexedContainer container = new IndexedContainer(); // Define the properties (columns) container.addContainerProperty("name", String.class, "noname"); container.addContainerProperty("volume", Double.class, -1.0d); // Add some items Object content[][] = { {"jar", 2.0}, {"bottle", 0.75}, {"can", 1.5}}; for (Object[] row: content) { Item newItem = container.getItem(container.addItem()); newItem.getItemProperty("name").setValue(row[0]); newItem.getItemProperty("volume").setValue(row[1]); } ---- New items are added with [methodname]#addItem()#, which returns the item ID of the new item, or by giving the item ID as a parameter as was described earlier. Note that the [classname]#Table# component, which has [classname]#IndexedContainer# as its default container, has a conveniency [methodname]#addItem()# method that allows adding items as object vectors containing the property values. The container implements the [interfacename]#Container.Indexed# feature to allow accessing the item IDs by their index number, with [methodname]#getIdByIndex()#, etc. The feature is required mainly for internal purposes of some components, such as [classname]#Table#, which uses it to enable lazy transmission of table data to the client-side. [[datamodel.container.beancontainer]] == [classname]#BeanContainer# The [classname]#BeanContainer# is an in-memory container for JavaBean objects. Each contained bean is wrapped inside a [classname]#BeanItem# wrapper. The item properties are determined automatically by inspecting the getter and setter methods of the class. This requires that the bean class has public visibility, local classes for example are not allowed. Only beans of the same type can be added to the container. The generic has two parameters: a bean type and an item identifier type. The item identifiers can be obtained by defining a custom resolver, using a specific item property for the IDs, or by giving item IDs explicitly. As such, it is more general than the [classname]#BeanItemContainer#, which uses the bean object itself as the item identifier, making the use usually simpler. Managing the item IDs makes [classname]#BeanContainer# more complex to use, but it is necessary in some cases where the [methodname]#equals()# or [methodname]#hashCode()# methods have been reimplemented in the bean. ---- // Here is a JavaBean public class Bean implements Serializable { String name; double energy; // Energy content in kJ/100g public Bean(String name, double energy) { this.name = name; this.energy = energy; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getEnergy() { return energy; } public void setEnergy(double energy) { this.energy = energy; } } void basic(VerticalLayout layout) { // Create a container for such beans with // strings as item IDs. BeanContainer beans = new BeanContainer(Bean.class); // Use the name property as the item ID of the bean beans.setBeanIdProperty("name"); // Add some beans to it beans.addBean(new Bean("Mung bean", 1452.0)); beans.addBean(new Bean("Chickpea", 686.0)); beans.addBean(new Bean("Lentil", 1477.0)); beans.addBean(new Bean("Common bean", 129.0)); beans.addBean(new Bean("Soybean", 1866.0)); // Bind a table to it Table table = new Table("Beans of All Sorts", beans); layout.addComponent(table); } ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beancontainer.basic[on-line example, window="_blank"]. To use explicit item IDs, use the methods [methodname]#addItem(Object, Object)#, [methodname]#addItemAfter(Object, Object, Object)#, and [methodname]#addItemAt(int, Object, Object)#. It is not possible to add additional properties to the container, except properties in a nested bean. [[datamodel.container.beancontainer.nestedproperties]] === Nested Properties ((("nested bean properties", id="term.datamodel.container.beancontainer.nestedproperties", range="startofrange"))) If you have a nested bean with an 1:1 relationship inside a bean type contained in a [classname]#BeanContainer# or [classname]#BeanItemContainer#, you can add its properties to the container by specifying them with [methodname]#addNestedContainerProperty()#. The feature is defined at the level of [classname]#AbstractBeanContainer#. ((("[methodname]#addNestedContainerProperty()#"))) As with the bean in a bean container, also a nested bean must have public visibility or otherwise an access exception is thrown. An intermediate reference from a bean in the bean container to a nested bean may have a null value. For example, let us assume that we have the following two beans with the first one nested inside the second one. ---- /** Bean to be nested */ public class EqCoord implements Serializable { double rightAscension; /* In angle hours */ double declination; /* In degrees */ ... setters and getters for the properties ... } /** Bean referencing a nested bean */ public class Star implements Serializable { String name; EqCoord equatorial; /* Nested bean */ ... setters and getters for the properties ... } ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beanitemcontainer.nestedbean[on-line example, window="_blank"]. After creating the container, you can declare the nested properties by specifying their property identifiers with the [methodname]#addNestedContainerProperty()# in dot notation. ---- // Create a container for beans BeanItemContainer stars = new BeanItemContainer(Star.class); // Declare the nested properties to be used in the container stars.addNestedContainerProperty("equatorial.rightAscension"); stars.addNestedContainerProperty("equatorial.declination"); // Add some items stars.addBean(new Star("Sirius", new EqCoord(6.75, 16.71611))); stars.addBean(new Star("Polaris", new EqCoord(2.52, 89.26417))); // Here the nested bean reference is null stars.addBean(new Star("Vega", null)); ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beanitemcontainer.nestedbean[on-line example, window="_blank"]. If you bind such a container to a [classname]#Table#, you probably also need to set the column headers. Notice that the entire nested bean itself is still a property in the container and would be displayed in its own column. The [methodname]#toString()# method is used for obtaining the displayed value, which is by default an object reference. You normally do not want this, so you can hide the column with [methodname]#setVisibleColumns()#. ((("[methodname]#setVisibleColumns()#"))) ---- // Put them in a table Table table = new Table("Stars", stars); table.setColumnHeader("equatorial.rightAscension", "RA"); table.setColumnHeader("equatorial.declination", "Decl"); table.setPageLength(table.size()); // Have to set explicitly to hide the "equatorial" property table.setVisibleColumns("name", "equatorial.rightAscension", "equatorial.declination"); ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beanitemcontainer.nestedbean[on-line example, window="_blank"]. The resulting table is shown in <>. [[figure.datamodel.container.beancontainer.nestedproperties]] .[classname]#Table# Bound to a [classname]#BeanContainer# with Nested Properties image::img/beanitemcontainer-nested-beans.png[] The bean binding in [classname]#AbstractBeanContainer# normally uses the [classname]#MethodProperty# implementation of the [classname]#Property# interface to access the bean properties using the setter and getter methods. For nested properties, the [classname]#NestedMethodProperty# implementation is used. ((("[classname]#MethodProperty#"))) ((("[classname]#NestedMethodProperty#"))) (((range="endofrange", startref="term.datamodel.container.beancontainer.nestedproperties"))) ifdef::web[] [[datamodel.container.beancontainer.idresolver]] === Defining a Bean ID Resolver If a bean ID resolver is set using [methodname]#setBeanIdResolver()# or [methodname]#setBeanIdProperty()#, the methods [methodname]#addBean()#, [methodname]#addBeanAfter()#, [methodname]#addBeanAt()# and [methodname]#addAll()# can be used to add items to the container. If one of these methods is called, the resolver is used to generate an identifier for the item (must not return [parameter]#null#). Note that explicit item identifiers can also be used when a resolver has been set by calling the [methodname]#addItem*()# methods - the resolver is only used when adding beans using the [methodname]#addBean*()# or [methodname]#addAll(Collection)# methods. endif::web[] [[datamodel.container.beanitemcontainer]] == [classname]#BeanItemContainer# [classname]#BeanItemContainer# is a container for JavaBean objects where each bean is wrapped inside a [classname]#BeanItem# wrapper. The item properties are determined automatically by inspecting the getter and setter methods of the class. This requires that the bean class has public visibility, local classes for example are not allowed. Only beans of the same type can be added to the container. [classname]#BeanItemContainer# is a specialized version of the [classname]#BeanContainer# described in <>. It uses the bean itself as the item identifier, which makes it a bit easier to use than [classname]#BeanContainer# in many cases. The latter is, however, needed if the bean has reimplemented the [methodname]#equals()# or [methodname]#hashCode()# methods. Let us revisit the example given in <> using the [classname]#BeanItemContainer#. ---- // Create a container for the beans BeanItemContainer beans = new BeanItemContainer(Bean.class); // Add some beans to it beans.addBean(new Bean("Mung bean", 1452.0)); beans.addBean(new Bean("Chickpea", 686.0)); beans.addBean(new Bean("Lentil", 1477.0)); beans.addBean(new Bean("Common bean", 129.0)); beans.addBean(new Bean("Soybean", 1866.0)); // Bind a table to it Table table = new Table("Beans of All Sorts", beans); ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.beanitemcontainer.basic[on-line example, window="_blank"]. It is not possible to add additional properties to a [classname]#BeanItemContainer#, except properties in a nested bean, as described in <>. ((("nested bean properties"))) ifdef::web[] [[datamodel.container.iterating]] == Iterating Over a Container As the items in a [classname]#Container# are not necessarily indexed, iterating over the items has to be done using an [classname]#Iterator#. The [methodname]#getItemIds()# method of [classname]#Container# returns a [classname]#Collection# of item identifiers over which you can iterate. The following example demonstrates a typical case where you iterate over the values of check boxes in a column of a [classname]#Table# component. The context of the example is the example used in <>. ---- // Collect the results of the iteration into this string. String items = ""; // Iterate over the item identifiers of the table. for (Iterator i = table.getItemIds().iterator(); i.hasNext();) { // Get the current item identifier, which is an integer. int iid = (Integer) i.next(); // Now get the actual item from the table. Item item = table.getItem(iid); // And now we can get to the actual checkbox object. Button button = (Button) (item.getItemProperty("ismember").getValue()); // If the checkbox is selected. if ((Boolean)button.getValue() == true) { // Do something with the selected item; collect the // first names in a string. items += item.getItemProperty("First Name") .getValue() + " "; } } // Do something with the results; display the selected items. layout.addComponent (new Label("Selected items: " + items)); ---- Notice that the [methodname]#getItemIds()# returns an __unmodifiable collection__, so the [classname]#Container# may not be modified during iteration. You can not, for example, remove items from the [classname]#Container# during iteration. The modification includes modification in another thread. If the [classname]#Container# is modified during iteration, a [classname]#ConcurrentModificationException# is thrown and the iterator may be left in an undefined state. endif::web[] [[datamodel.container.gpc]] == [classname]#GeneratedPropertyContainer# [classname]#GeneratedPropertyContainer# is a container wrapper that allows defining generated values for properties (columns). The generated properties can shadow properties with the same IDs in the wrapped container. Removing a property from the wrapper hides it. The container is especially useful with [classname]#Grid#, which does not support generated columns or hiding columns like [classname]#Table# does. [[datamodel.container.gpc.wrapping]] === Wrapping a Container A container to be wrapped must be a [interfacename]#Container.Indexed#. It can optionally also implement [interfacename]#Container.Sortable# or [interfacename]#Container.Filterable# to enable sorting and filtering the container, respectively. For example, let us consider the following container with some regular columns: ---- IndexedContainer container = new IndexedContainer(); container.addContainerProperty("firstname", String.class, null); container.addContainerProperty("lastname", String.class, null); container.addContainerProperty("born", Integer.class, null); container.addContainerProperty("died", Integer.class, null); // Wrap it GeneratedPropertyContainer gpcontainer = new GeneratedPropertyContainer(container); ---- [[datamodel.container.gpc.properties]] === Generated Properties Now, you can add generated properties in the container with [methodname]#addGeneratedProperty()# by specifying a property ID and a [interfacename]#PropertyValueGenerator#. The method takes the ID of the generated property as first parameter; you can use a same ID as in the wrapped container to shadow its properties. You need to implement [methodname]#getType()#, which must return the class object of the value type of the property, and [methodname]#getValue()#, which returns the property value for the given item. The item ID and the property ID of the generated property are also given in case they are needed. You can access other properties of the item to compute the property value. ---- gpcontainer.addGeneratedProperty("lived", new PropertyValueGenerator() { @Override public Integer getValue(Item item, Object itemId, Object propertyId) { int born = (Integer) item.getItemProperty("born").getValue(); int died = (Integer) item.getItemProperty("died").getValue(); return Integer.valueOf(died - born); } @Override public Class getType() { return Integer.class; } }); ---- You can access other items in the container, also their generated properties, although you should beware of accidental recursion. [[datamodel.container.gpc.using]] === Using [classname]#GeneratedPropertyContainer# Finally, you need to bind the [classname]#GeneratedPropertyContainer# to the component instead of the wrapped container. ---- Grid grid = new Grid(gpcontainer); ---- When using [classname]#GeneratedPropertyContainer# in [classname]#Grid#, notice that generated columns are read-only, so you can not add grid rows with [methodname]#addRow()#. In editable mode, editor fields are not generated for generated columns. [[datamodel.container.gpc.sorting]] === Sorting Even though the [classname]#GeneratedPropertyContainer# implements [interfacename]#Container.Sortable#, the wrapped container must also support it or otherwise sorting is disabled. Also, the generated properties are not normally sortable, but require special handling to enable sorting. [[datamodel.container.filtered]] == [classname]#Filterable# Containers ((("Container", "Filterable", id="term.datamodel.container.filtered.filterable", range="startofrange"))) ((("[classname]#Filter# (in [classname]#Container#)", id="term.datamodel.container.filtered.filters", range="startofrange"))) Containers that implement the [classname]#Container.Filterable# interface can be filtered. For example, the built-in [classname]#IndexedContainer# and the bean item containers implement it. Filtering is typically used for filtering the content of a [classname]#Table#. ((("[classname]#IndexedContainer#"))) ((("[classname]#Table#"))) Filters implement the [classname]#Filter# interface and you add them to a filterable container with the [methodname]#addContainerFilter()# method. Container items that pass the filter condition are kept and shown in the filterable component. ((("[methodname]#addContainerFilter()#"))) ---- Filter filter = new SimpleStringFilter("name", "Douglas", true, false); table.addContainerFilter(filter); ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.filter.basic[on-line example, window="_blank"]. If multiple filters are added to a container, they are evaluated using the logical AND operator so that only items that are passed by all the filters are kept. [[datamodel.container.filtered.composite]] === Atomic and Composite Filters Filters can be classified as __atomic__ and __composite__. Atomic filters, such as [classname]#SimpleStringFilter#, define a single condition, usually for a specific container property. Composite filters make filtering decisions based on the result of one or more other filters. The built-in composite filters implement the logical operators AND, OR, or NOT. For example, the following composite filter would filter out items where the [literal]#++name++# property contains the name "Douglas" somewhere __or__ where the [literal]#++age++# property has value less than 42. The properties must have [classname]#String# and [classname]#Integer# types, respectively. ---- filter = new Or(new SimpleStringFilter("name", "Douglas", true, false), new Compare.Less("age", 42)); ---- [[datamodel.container.filtered.builtin]] === Built-In Filter Types The built-in filter types are the following: [classname]#SimpleStringFilter#:: ((("[classname]#SimpleStringFilter#"))) + Passes items where the specified property, that must be of [classname]#String# type, contains the given [parameter]#filterString# as a substring. If [parameter]#ignoreCase# is [parameter]#true#, the search is case insensitive. If the [parameter]#onlyMatchPrefix# is [parameter]#true#, the substring may only be in the beginning of the string, otherwise it may be elsewhere as well. [classname]#IsNull#:: ((("[classname]#IsNull# (filter)"))) + Passes items where the specified property has null value. For in-memory filtering, a simple [literal]#++==++# check is performed. For other containers, the comparison implementation is container dependent, but should correspond to the in-memory null check. [classname]#Equal#,[classname]#Greater#, [classname]#Less#, [classname]#GreaterOrEqual#, and[classname]#LessOrEqual#:: ((("[classname]#Equal# (filter)"))) ((("[classname]#Greater# (filter)"))) ((("[classname]#Less# (filter)"))) ((("[classname]#GreaterOrEqual# (filter)"))) ((("[classname]#LessOrEqual# (filter)"))) + The comparison filter implementations compare the specified property value to the given constant and pass items for which the comparison result is true. The comparison operators are included in the abstract [classname]#Compare# class. + For the [classname]#Equal# filter, the [methodname]#equals()# method for the property is used in built-in in-memory containers. In other types of containers, the comparison is container dependent and may use, for example, database comparison operations. + For the other filters, the property value type must implement the [classname]#Comparable# interface to work with the built-in in-memory containers. Again for the other types of containers, the comparison is container dependent. [classname]#And#and[classname]#Or#:: ((("[classname]#And# (filter)"))) ((("[classname]#Or# (filter)"))) + These logical operator filters are composite filters that combine multiple other filters. [classname]#Not#:: ((("[classname]#Not# (filter)"))) + The logical unary operator filter negates which items are passed by the filter given as the parameter. [[datamodel.container.filtered.custom]] === Implementing Custom Filters A custom filter needs to implement the [classname]#Container.Filter# interface. A filter can use a single or multiple properties for the filtering logic. The properties used by the filter must be returned with the [methodname]#appliesToProperty()# method. If the filter applies to a user-defined property or properties, it is customary to give the properties as the first argument for the constructor of the filter. ---- class MyCustomFilter implements Container.Filter { protected String propertyId; protected String regex; public MyCustomFilter(String propertyId, String regex) { this.propertyId = propertyId; this.regex = regex; } /** Tells if this filter works on the given property. */ @Override public boolean appliesToProperty(Object propertyId) { return propertyId != null && propertyId.equals(this.propertyId); } ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.filter.custom[on-line example, window="_blank"]. The actual filtering logic is done in the [methodname]#passesFilter()# method, which simply returns [literal]#++true++# if the item should pass the filter and [literal]#++false++# if it should be filtered out. ---- /** Apply the filter on an item to check if it passes. */ @Override public boolean passesFilter(Object itemId, Item item) throws UnsupportedOperationException { // Acquire the relevant property from the item object Property p = item.getItemProperty(propertyId); // Should always check validity if (p == null || !p.getType().equals(String.class)) return false; String value = (String) p.getValue(); // The actual filter logic return value.matches(regex); } } ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.filter.custom[on-line example, window="_blank"]. You can use such a custom filter just like any other: ---- c.addContainerFilter( new MyCustomFilter("Name", (String) tf.getValue())); ---- See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.container.filter.custom[on-line example, window="_blank"]. (((range="endofrange", startref="term.datamodel.container.filtered.filters"))) (((range="endofrange", startref="term.datamodel.container.filtered.filterable"))) (((range="endofrange", startref="term.datamodel.container")))