1#------------------------------------------------------------------------------- 2# 3# An abstract base class implementation of the ITemplateDataNameItem interface 4# that looks for all specified values in its input context or optionally any of 5# its sub-contexts and outputs a context containing all such values found. 6# 7# Written by: David C. Morrill 8# 9# Date: 07/29/2007 10# 11# (c) Copyright 2007 by Enthought, Inc. 12# 13#------------------------------------------------------------------------------- 14 15""" An abstract base class implementation of the ITemplateDataNameItem interface 16 that looks for all specified values in its input context or optionally any 17 of its sub-contexts and outputs a context containing all such values found. 18""" 19 20#------------------------------------------------------------------------------- 21# Imports: 22#------------------------------------------------------------------------------- 23 24from traits.api \ 25 import HasPrivateTraits, Instance, Property, provides 26 27from apptools.template.template_traits \ 28 import TBool 29 30from apptools.template.itemplate_data_context \ 31 import ITemplateDataContext 32 33from apptools.template.itemplate_data_name_item \ 34 import ITemplateDataNameItem 35 36from apptools.template.template_impl \ 37 import Template 38 39from .template_data_context \ 40 import TemplateDataContext 41 42from .helper \ 43 import path_for 44 45#------------------------------------------------------------------------------- 46# 'AnyDataNameItem' class: 47#------------------------------------------------------------------------------- 48 49class AnyDataNameItem ( Template ): 50 """ An abstract base class implementation of the ITemplateDataNameItem 51 interface that looks for all specified values in its input context or 52 optionally any of its sub-contexts and outputs a context containing all 53 such values found. 54 """ 55 56 implements ( ITemplateDataNameItem ) 57 58 #-- 'ITemplateDataNameItem' Interface Implementation ----------------------- 59 60 # The data context which this data name item should match against: 61 input_data_context = Instance( ITemplateDataContext ) 62 63 # The data context containing the data values and/or contexts this data 64 # name item matches: 65 output_data_context = Instance( ITemplateDataContext ) 66 67 # The ITemplateChoice instance representing the current settings of the 68 # data name item. This value must be read/write, and must be overridden by 69 # sublasses. 70 data_name_item_choice = Property 71 72 # The alternative choices the user has for the data name item settings for 73 # the current input data context. The list may be empty, in which case the 74 # user cannot change the settings of the data name item. This value can be 75 # read only, and must be overridden by subclasses. 76 data_name_item_choices = Property 77 78 #-- Public Traits ---------------------------------------------------------- 79 80 # Should all sub-contexts be included in the search: 81 recursive = TBool( False ) 82 83 # Should included sub-contexts be flattened into a single context? 84 flatten = TBool( False ) 85 86 #-- Private Traits --------------------------------------------------------- 87 88 # The current recursive setting: 89 current_recursive = TBool( False ) 90 91 # The current input data context: 92 current_input_data_context = Property 93 94 #-- Abstract Methods (Must be overridden in subclasses) -------------------- 95 96 def filter ( self, name, value ): 97 """ Returns **True** if the specified context data *name* and *value* 98 should be included in the output context; and **False** otherwise. 99 """ 100 raise NotImplementedError 101 102 #-- Property Implementations ----------------------------------------------- 103 104 def _get_data_name_item_choice ( self ): 105 raise NotImplementedError 106 107 def _set_data_name_item_choice ( self, value ): 108 raise NotImplementedError 109 110 def _get_data_name_item_choices ( self ): 111 raise NotImplementedError 112 113 def _get_current_input_data_context ( self ): 114 return self.input_data_context 115 116 #-- Trait Event Handlers --------------------------------------------------- 117 118 def _recursive_changed ( self, value ): 119 """ Handles the primary recursive setting being changed. 120 """ 121 self.current_recursive = value 122 123 def _input_data_context_changed ( self ): 124 """ Handles the 'input_data_context' trait being changed. 125 """ 126 self.inputs_changed() 127 128 #-- Private Methods -------------------------------------------------------- 129 130 def inputs_changed ( self ): 131 """ Handles any input value being changed. This method should be called 132 by subclasses when any of their input values change. 133 """ 134 output_context = None 135 input_context = self.current_input_data_context 136 if input_context is not None: 137 values = {} 138 139 if self.current_recursive: 140 if self.flatten: 141 self._add_context( input_context, values ) 142 else: 143 self._copy_context( input_context, values ) 144 else: 145 self._add_values( input_context, values, '' ) 146 147 if len( values ) > 0: 148 output_context = TemplateDataContext( 149 data_context_path = input_context.data_context_path, 150 data_context_name = input_context.data_context_name, 151 values = values ) 152 153 self.output_data_context = output_context 154 155 def _add_values ( self, input_context, values, path = '' ): 156 """ Adds all of the matching values in the specified *input_context* to 157 the specified *values* dictionary. 158 """ 159 # Filter each name/value in the current input context to see if it 160 # should be added to the output values: 161 filter = self.filter 162 gdcv = input_context.get_data_context_value 163 for name in input_context.data_context_values: 164 value = gdcv( name ) 165 if self.filter( name, value ): 166 values[ path_for( path, name ) ] = value 167 168 def _add_context ( self, input_context, values, path = '' ): 169 """ Adds all of the matching values in the specified *input_context* to 170 the specified *output_context*, and then applies itself recursively 171 to all contexts contained in the specified *input_context*. 172 """ 173 # Add all of the filtered values in the specified input context: 174 self._add_values( input_context, values, path ) 175 176 # Now process all of the input context's sub-contexts: 177 gdc = input_context.get_data_context 178 for name in input_context.data_contexts: 179 self._add_context( gdc( name ), values, path_for( path, 180 input_context.data_context_name ) ) 181 182 def _copy_context ( self, input_context ): 183 """ Clone the input context so that the result only contains values and 184 contexts which contain valid values and are not empty. 185 """ 186 values = {} 187 contexts = {} 188 189 # Add all of the filtered values in the specified input context: 190 self._add_values( input_context, values ) 191 192 # Now process all of the input context's sub-contexts: 193 gdc = input_context.get_data_context 194 for name in input_context.data_contexts: 195 context = self._copy_context( gdc( name ) ) 196 if context is not None: 197 contexts[ name ] = context 198 199 if (len( values ) == 0) and (len( contexts ) == 0): 200 return None 201 202 return TemplateDataContext( 203 data_context_path = input_context.data_context_path, 204 data_context_name = input_context.data_context_name, 205 values = values, 206 contexts = contexts ) 207 208