package gw.solr.request uses gw.api.system.PLLoggerCategory uses gw.lang.reflect.IType uses gw.pl.persistence.core.Bean uses gw.solr.mapper.IDataHolder uses gw.xsd.config.solr_search_config.DataProperty uses gw.xsd.config.solr_search_config.Root uses java.lang.Character uses java.lang.StringBuilder uses java.util.Collection uses java.util.HashMap uses java.util.LinkedHashSet uses java.util.Set uses java.util.Map uses java.lang.Exception uses java.util.HashSet uses java.util.List @ReadOnly class DataHolderImpl implements IDataHolder { var _storage = new HashMap() var _rootNameToTypeMap : Map as RootMap = new HashMap() var _beans : Set as readonly ReferencedBeans = new LinkedHashSet() var _collectReferences = false construct() { } construct(collectRefs : boolean) { _collectReferences = collectRefs } construct( rootDescs : List, rootObj : Object ) { loadRootMap( rootDescs ) addData( rootObj ) } construct( rootDescs : List, rootObjs : List ) { loadRootMap( rootDescs ) loadData( rootObjs ) } final function loadRootMap(roots : List) { roots.each( \ r -> addRoot(r)) } final function addRoot( rootDesc : Root ) { _rootNameToTypeMap.put(rootDesc.Name, rootDesc.Type) } final function loadData( objs : List ) { objs.each( \ rb -> addData(rb) ) } final function addData(obj : Object) { if(obj != null) { var indexDataType : IType = typeof obj var indexDataSuperType = indexDataType var circularTypeCatcher = new HashSet() while(indexDataSuperType != null) { if(circularTypeCatcher.contains(indexDataSuperType.Name)) { break } circularTypeCatcher.add(indexDataSuperType.Name) if(containsKey(indexDataSuperType.Name)) { throw new Exception("Duplicate key added to data holder for type " + indexDataType.Name + ". Use path based configuration or a unique virtual key to resolve this error.") } put(indexDataSuperType.Name, obj) indexDataSuperType = indexDataSuperType.Supertype } } } function containsKey( key : String ) : boolean { return _storage.containsKey( key ) } function put( key : String, data : Object ) { _storage.put( key, data ) } override function addReference( bean : KeyableBean ) { if(_collectReferences) { _beans.add(bean) } } override function addReferences( beans : Collection ) { if(_collectReferences) { _beans.addAll(beans) } } override reified function lookup( dp : DataProperty ) : T { var value = lookupImpl( dp ) return value typeis T ? value : null } override reified function lookup( dp : DataProperty, defaultValue : T ) : T { var value = lookupImpl( dp, defaultValue ) return value typeis T ? value : null } override function lookupKey( dp : DataProperty ) : String { var sb = new StringBuilder(128) if(dp.Path != null) { var walker = new DataHolderPath(dp.Path) if(walker.next() != null && walker.Field.length() > 0) { appendCamelCase(sb, walker.Field) while(walker.next() != null) { sb.append(walker.Field) } } else { PLLoggerCategory.SOLR.warn("Unqualified key field. Path is empty.") // invalid key field spec in configuration } } else if(dp.Name != null) { if(dp.Name.length() > 0) { appendCamelCase(sb, asRelativeName(dp.Name)) sb.appendIf(dp.Property != null, dp.Property) } else { PLLoggerCategory.SOLR.warn("Unqualified key field. Name is empty.") // invalid key field spec in configuration } } else { PLLoggerCategory.SOLR.warn("Unqualified key field.") // invalid key field spec in configuration } return sb.length() > 0 ? sb.toString() : dp.Property } override function isChanged( dp : DataProperty ) : boolean { var result = false if(dp.Path != null) { result = findChangesByPath( dp.Path ) } else if(dp.Name != null) { result = findChangesByName( dp, dp.Name ) } return result } private function findChangesByPath( path : String ) : boolean { if(path.length() == 0) { return false } var walker = new DataHolderPath(path) var parent : Object = findRoot(walker, path) while(walker.next() != null && parent != null) { if(parent typeis Bean) { if(parent typeis KeyableBean) { if(parent.New || parent.ChangedFields.contains(walker.Field)) { return true } } else { PLLoggerCategory.SOLR.trace("Skipping non-keyable bean of type " + typeof parent) } } var candidate = parent[walker.Field] if(!walker.isValidChild(candidate, parent)) { return false } parent = candidate } return false } private function findChangesByName( dp : DataProperty, name : String ) : boolean { if(name.length() == 0) { return false } var instance : Object = _storage.get(name) if(instance typeis Bean) { if(instance typeis KeyableBean) { if(instance.New || instance.ChangedFields.contains(dp.Property)) { return true } } else { PLLoggerCategory.SOLR.trace("Skipping non-keyable bean of type " + typeof instance) } } return false } private function lookupImpl( dp : DataProperty ) : Object { return !isEmpty(dp.Path) ? lookupByPath(dp) : lookupByName( dp ) } private function lookupImpl( dp : DataProperty, defaultValue : Object ) : Object { var result = lookup( dp ) return result != null ? result : defaultValue } private static function isEmpty( s : String ) : boolean { return s == null || s.Empty } private function lookupByName( dp : DataProperty ) : Object { var result : Object = null var instance = _storage.get(dp.Name) if(instance != null) { var prop = dp.Property if(prop != null && prop.length() > 0) { result = instance[prop] } else { result = instance } } if(_collectReferences) { if(result typeis KeyableBean) { _beans.add(result) } else if(result != null && instance typeis KeyableBean) { _beans.add(instance) } } return result } private function lookupByPath( dp : DataProperty ) : Object { var walker = new DataHolderPath(dp.Path) var bean : Object = findRoot(walker, dp.Path) var owner : Object = null while(walker.next() != null && bean != null) { var candidate = bean[walker.Field] owner = bean bean = walker.isValidChild(candidate, bean) ? candidate : null } if(_collectReferences) { if(bean typeis KeyableBean) { _beans.add(bean) } else if(bean != null && owner typeis KeyableBean) { _beans.add(owner) } } return bean } private function findRoot(walker : DataHolderPath, path : String) : Object { var rootName = walker.Root if(rootName == null) { throw new Exception("No root name in path `" + path + "'") } var rootType = _rootNameToTypeMap.get(rootName) if(rootType == null) { throw new Exception("No root found with name `" + rootName + "' for path '" + path + "'") } return _storage.get(rootType) } private function asRelativeName( name : String ) : String { return name != null ? name.substring(name.lastIndexOf('.') + 1) : null } private function appendCamelCase( sb : StringBuilder, str : String ) : String { var result = "" if(str != null && str.length() > 0) { sb.append(Character.toLowerCase(str.charAt(0))) if(str.length() > 1) { sb.append(str.substring(1)) } } return result } }