package com.k_int.openxsd.analysis;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDAttributeUse;
import org.eclipse.xsd.XSDComplexTypeContent;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDCompositor;
import org.eclipse.xsd.XSDContentTypeCategory;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDEnumerationFacet;
import org.eclipse.xsd.XSDModelGroup;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDParticleContent;
import org.eclipse.xsd.XSDPatternFacet;
import org.eclipse.xsd.XSDRedefine;
import org.eclipse.xsd.XSDRedefineContent;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDSchemaContent;
import org.eclipse.xsd.XSDSchemaDirective;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTerm;
import org.eclipse.xsd.XSDTypeDefinition;
import org.eclipse.xsd.XSDWildcard;
import org.eclipse.xsd.util.XSDParser;
import org.eclipse.xsd.util.XSDUtil;


import com.k_int.openxsd.analysis.iface.SchemaAnalysis;
import com.k_int.openxsd.core.AttributeRef;
import com.k_int.openxsd.core.ElementDeclaration;
import com.k_int.openxsd.core.ElementRef;
import com.k_int.openxsd.core.NameRef;
import com.k_int.openxsd.core.TypeDefinition;

public class SchemaAnalyser
{
    private XSDParser parser;
    private List<String> processed_locations                    = new ArrayList<String>();
    private Map<String,ElementDeclaration> processed_elements   = new HashMap<String,ElementDeclaration>();
    private Map<String,TypeDefinition>     processed_type_defs  = new HashMap<String,TypeDefinition>();
   
    private Map<String,XSDElementDeclaration> xsd_elements      = new HashMap<String,XSDElementDeclaration>();
    private Map<String,XSDTypeDefinition> xsd_types             = new HashMap<String,XSDTypeDefinition>();
    private List<XSDElementDeclaration> possible_roots          = new ArrayList<XSDElementDeclaration>();
    
    
    public void populateAnalysisObject(SchemaAnalysis analysis, File base_schema_file) throws AnalysisException
    {
        try // parse the schema and set the namespaces
        {
            parser                  = new XSDParser(new HashMap<Object,Object>());
            parser.parse(new FileInputStream(base_schema_file));
            XSDSchema root_schema   = parser.getSchema(); 
            root_schema.setSchemaLocation(base_schema_file.getParent());
            analysis.setTargetNamespace(root_schema.getTargetNamespace());
            analysis.setNsToPrefixMap(reverseNSMap(root_schema.getQNamePrefixToNamespaceMap()));
            processSchema(root_schema);
            analysePossibleRoots();
            walkTheSchema();            
            createUnreferencedTypesAndElements();
            populateSchemaAnalysis(analysis);
            
        }
        catch(Throwable e)
        {
            e.printStackTrace();
            throw new AnalysisException(e);
        }
    }
    
    
    private void populateSchemaAnalysis(SchemaAnalysis analysis)
    {
        Iterator<ElementDeclaration> elements = processed_elements.values().iterator();
        while(elements.hasNext())
        {
            analysis.addElement(elements.next());            
        }
        
        Iterator<TypeDefinition> defs = processed_type_defs.values().iterator();
     
        while(defs.hasNext())
        {
            analysis.addType(defs.next());     
        }
        
        Iterator<XSDElementDeclaration> roots = possible_roots.iterator();
        while(roots.hasNext())
        {
            XSDElementDeclaration root = roots.next();
            analysis.addPossibleRoot(new NameRef(root.getName(),root.getTargetNamespace()));
        }        
    }
    
    // the map is provided prefix to ns - we want ns to prefix
    private Map<String,String> reverseNSMap(Map<String,String> prefix_ns_map)
    {
        Map<String,String> ns_prefix_map = new HashMap<String,String>();
        Iterator<Map.Entry<String, String>> i = prefix_ns_map.entrySet().iterator();
        while(i.hasNext())
        {
            Map.Entry<String, String> val = i.next();
            ns_prefix_map.put(val.getValue(), val.getKey());
        }
        return ns_prefix_map;
    }
    
    
    private void processSchema(XSDSchema schema) throws AnalysisException
    {
        Iterator<XSDTypeDefinition> types = schema.getTypeDefinitions().iterator();
       
        // add all the global types to the processing list (if not already added)
        while(types.hasNext())
        {
            XSDTypeDefinition xsd_type = types.next();
            String name                         = xsd_type.getName();
            String ns_uri                       = xsd_type.getTargetNamespace();
          
            if(xsd_types.get(ns_uri+name)==null) // not already processed
                xsd_types.put(ns_uri+name,xsd_type);
        }
                
        // add all the global elements to the processing list if not already added
        Iterator<XSDElementDeclaration> elements = schema.getElementDeclarations().iterator(); 
        while(elements.hasNext())
        {
            XSDElementDeclaration xsd_element   = elements.next();
            String name                         = xsd_element.getName();
            String ns_uri                       = xsd_element.getTargetNamespace();
          
            if(xsd_elements.get(ns_uri+name)==null) // not already processed
                xsd_elements.put(ns_uri+name,xsd_element);   
        }
         
        // get the general schema content 
        Iterator<XSDSchemaContent> content_iterator = schema.getContents().iterator();
        while(content_iterator.hasNext())
        {
            XSDSchemaContent content = content_iterator.next();
                
            // redefined model group,type or attribute group
            // we add this to the elements and types
            // before we process the redefined schema
            // this way we ensure that the redefinition
            // is used in preference to the item from the original schema
            if (content instanceof XSDRedefine) 
            {                
                XSDRedefine redefine_element = (XSDRedefine) content;
                // load the redefined schema
                loadReferencedSchema(schema,redefine_element.getSchemaLocation());
                Iterator<XSDRedefineContent> redefined_content = redefine_element.getContents().iterator();
                while(redefined_content.hasNext())
                {
                    XSDRedefineContent redefine = redefined_content.next();
                    // could be model group redefine (currently not processed)
                    // type redefine
                    // attribute group (currently not processed)
                    if(redefine instanceof XSDTypeDefinition) // redefined type
                    {
                        
                        XSDTypeDefinition redefined_def = (XSDTypeDefinition)redefine;
                        String name                         = redefined_def.getName();
                        System.out.println("---- Redefining type "+name+" "+redefined_def);
                        String ns_uri                       = redefined_def.getTargetNamespace();
                        // in this case as it is a redefine we overwrite existing assignment
                        xsd_types.put(ns_uri+name,redefined_def);                       
                    }
                }
               
            }
            else if (content instanceof XSDSchemaDirective) // referenced schema
            {
                XSDSchemaDirective schema_directive = (XSDSchemaDirective) content;                              
                String referenced_location          = schema_directive.getSchemaLocation();
                loadReferencedSchema(schema,referenced_location);               
            }
        }       
        
    }
    
    private void loadReferencedSchema(XSDSchema base,String referenced_location)throws AnalysisException
    {
        String http_loc="";
        if(referenced_location.startsWith("http://"))
        {
            http_loc=referenced_location;
            referenced_location = referenced_location.substring(referenced_location.lastIndexOf("/")+1, referenced_location.length());
        }
        
        // already processed (XSD library sometimes gets in a loop!!)
        if(processed_locations.contains(referenced_location))
            return;
        
        String location             = base.getSchemaLocation();
        File ref_file = new File(location+"/"+referenced_location);
        try
        {
            parser.parse(new FileInputStream(ref_file));
        }
        catch(Exception e)
        {
            
            if(http_loc.startsWith("http://"))
                throw new AnalysisException("Reference to remote schema "+http_loc+" this must be saved in the same directory as the base schema");
            else
                throw new AnalysisException("Cant locate referenced schema "+referenced_location,e);
        }
        XSDSchema referenced_schema = parser.getSchema();
        referenced_schema.setSchemaLocation(ref_file.getParent());
        processed_locations.add(referenced_location);
        processSchema(referenced_schema); // perform analysis on the referenced schema
                 
    }
    
    // find out which of the global elements are cross referenced in a 
    // type - or redefined type
    // if not they are a possible root (xml entry point)
    // for any doc based on this schema
    private void analysePossibleRoots()
    {
        Iterator<XSDElementDeclaration> elements = xsd_elements.values().iterator();
        
        while(elements.hasNext())
        {
            XSDElementDeclaration element       = elements.next();            
            Iterator<XSDTypeDefinition> type_iterator = xsd_types.values().iterator();           
            int x_ref_counter                   = 0;
            
            while(type_iterator.hasNext())
            {
                XSDTypeDefinition type_def      = type_iterator.next();
                Collection<?> the_collection    = XSDUtil.UsageCrossReferencer.find(element,type_def);
                x_ref_counter                   = x_ref_counter+the_collection.size();
            }
            
           /** if (x_ref_counter==0)
            {
                Iterator<XSDRedefine> i = redefined_content.iterator();
                while(i.hasNext())
                {
                    XSDRedefine redefine = (XSDRedefine) i.next();
                    EList<XSDRedefineContent> redefine_content = redefine.getContents();
                    Collection<?> the_collection = XSDUtil.UsageCrossReferencer.find(element,redefine_content);
                    x_ref_counter = x_ref_counter+the_collection.size();
                }
            }**/
            if(x_ref_counter==0)
            {
              possible_roots.add(element);                
            }
        }                   
    }
    
    // start to walk the schema model and create our internal representation
    // using the possible roots as a starting point
    private void walkTheSchema()
    {
        Iterator<XSDElementDeclaration> i = possible_roots.iterator();
        while(i.hasNext())
        {
            System.out.println("Possible root ----------");
            XSDElementDeclaration element   = i.next();
            ElementDeclaration ki_element   = buildElementDec(element);
            assessElementType(ki_element,element);
        }
    }
    
    
    private void createUnreferencedTypesAndElements()
    {
        Iterator<XSDTypeDefinition> i = xsd_types.values().iterator();
        while(i.hasNext())
        {
            XSDTypeDefinition type = i.next();
            String name = type.getName();
            String ns = type.getTargetNamespace();
            if(processed_type_defs.get(ns+name)==null)
                processTypeDef(type);
            
        }    
        
        Iterator<XSDElementDeclaration> j = xsd_elements.values().iterator();
        while(j.hasNext())
        {
            XSDElementDeclaration element = j.next();
            String name = element.getName();
            String ns = element.getTargetNamespace();
            if(xsd_elements.get(ns+name)==null)
                buildElementDec(element);            
        }       
    }
    
    
    private void assessElementType(ElementDeclaration ki_element,XSDElementDeclaration xsd_element)
    {
        if(ki_element.getType()==null) // type not yet processed
        {
            XSDTypeDefinition original_def      = xsd_element.getTypeDefinition();           
            String def_name                     = original_def.getName();
            
            if(def_name==null) // anonymous declaration - synthesise the name
            {
                def_name = buildAnonTypeDefName(ki_element.getName(),original_def);
                original_def.setName(def_name);
            }
            
            // if available we must get the type from our list
            // it could have been redefined and this type
            // will still hold a reference to the original type
            XSDTypeDefinition def = xsd_types.get(original_def.getTargetNamespace()+original_def.getName());
            if(def==null)
                def=original_def; // if not present use the original
          
            NameRef ref = new NameRef(def_name,def.getTargetNamespace());
            ki_element.setType(ref);           
            processTypeDef(def);               
        }
    }
    
    
    
    // do all the housekeeping around creating an element declaration
    private ElementDeclaration buildElementDec(XSDElementDeclaration xsd_element)
    {
        String ns                       = xsd_element.getTargetNamespace();
        String name                     = xsd_element.getName();
        ElementDeclaration ki_element   = processed_elements.get(ns+name);
        System.out.println("Building element dec "+ns+name);
        if(ki_element==null)
        {          
            ki_element = new ElementDeclaration(name,ns); 
            processed_elements.put(ns+name, ki_element);
        }
        return ki_element;
    }
    
    
    private TypeDefinition processTypeDef(XSDTypeDefinition xsd_type)
    {
        if(xsd_type==null)
            return null;
        
        String name             = xsd_type.getName();
        String ns_uri           = xsd_type.getTargetNamespace();   
        TypeDefinition ki_def   = processed_type_defs.get(ns_uri+name);
        
        if(ki_def==null)
        {        
            ki_def = new TypeDefinition(name,ns_uri);
            processed_type_defs.put(ns_uri+name,ki_def); // ensure it isn't reprocessed
            
            if(xsd_type instanceof XSDSimpleTypeDefinition)
            {
                ki_def.setType(TypeDefinition.SIMPLE);
                analyseSimpleType((XSDSimpleTypeDefinition)xsd_type,ki_def);
            }
            else
            {
                ki_def.setType(TypeDefinition.COMPLEX);
                analyseComplexType((XSDComplexTypeDefinition)xsd_type,ki_def, false);
            }
        }
        return ki_def;
    }
    
    private void analyseComplexType(XSDComplexTypeDefinition xsd_type, TypeDefinition ki_def,boolean traverse)
    {
        System.out.println("Analyse complex type "+xsd_type);
        //XSDTypeDefinition xsd_base_type = xsd_type.getBaseType();
        XSDTypeDefinition xsd_root_type = xsd_type.getRootType();
        
       // XSDTypeDefinition xsd_derived   = null;
        
        System.out.println("Root type "+xsd_root_type);
       // System.out.println("Base type "+xsd_base_type);
        
        
        /**if(xsd_r_type!=null)
            xsd_derived = xsd_base_type;
        else if (xsd_root_type!=null)
            xsd_derived = xsd_root_type;**/
        
       // addAttributeUses(xsd_derived,ki_def);
       // addAttributeUses(xsd_type,ki_def);
        
        XSDComplexTypeContent the_content   = xsd_type.getContent();
        XSDContentTypeCategory the_category = xsd_type.getContentTypeCategory();
        
        if (the_category==XSDContentTypeCategory.ELEMENT_ONLY_LITERAL)
        {
            if(the_content==null) 
                analyseEmptyLiteral(xsd_type, ki_def);             
            else                        
                analyseMixedOrElement(xsd_type, ki_def);
        }
        else if(the_category==XSDContentTypeCategory.EMPTY_LITERAL)
        {
            analyseEmptyLiteral(xsd_type, ki_def);
        }
        else if(the_category==XSDContentTypeCategory.MIXED_LITERAL)
        {
            analyseMixedOrElement(xsd_type, ki_def);
        }       
        else if(the_category==XSDContentTypeCategory.SIMPLE_LITERAL)
        {
           
            XSDSimpleTypeDefinition xsd_type_def = (XSDSimpleTypeDefinition)the_content;
            if (xsd_type_def.getName()==null)
                xsd_type_def.setName(xsd_type.getName());
                
                
                //System.out.println(xsd_complex_type_def.getDerivationMethod().getName());
           //analyseSimpleType(xsd_type_def,k_int_type_def, xsd_complex_type_def.getDerivationMethod().getName());
            analyseSimpleType(xsd_type.getSimpleType(),ki_def);
        }  
        
    }
    
    private void analyseMixedOrElement(XSDComplexTypeDefinition def,TypeDefinition ki_def)
    {
        System.out.println("Mixed or element");
        XSDParticle particle = (XSDParticle)def.getContent();
        addAttributeUses(def,ki_def);
        // use the model of the parent particle here for the min occurs of the children ......
        
        XSDParticleContent particle_content = particle.getContent();
        System.out.println(particle_content);
        if (particle_content instanceof XSDModelGroup)
        {
            XSDModelGroup model = (XSDModelGroup) particle_content;          
            processModelGroup(model, ki_def, particle.getMinOccurs(), particle.getMaxOccurs());
        }
       
    }
    
    // process model group. Max occurs and min occurs only change for a choice
    // for a sequence always 1 and 1
    // this means that an element in the sequence with min occurs 0 (for example)
    // must override the model group min and max occurs
    private void processModelGroup(XSDModelGroup model_group, TypeDefinition ki_def, int group_min_occurs, int group_max_occurs)
    { 
         EList<XSDParticle> model_contents= model_group.getContents();   
         System.out.println("Process model group "+model_group.getCompositor().getName() +": "+group_min_occurs+": "+group_max_occurs);
         
         if (model_contents.size()==0)
              processModelGroupDefinition(model_group, ki_def, group_min_occurs, group_max_occurs);
         
         int group_type = model_group.getCompositor().getValue();
         
         Iterator<XSDParticle> i      = model_contents.iterator();
         while(i.hasNext())
         {
            XSDParticle local_particle  = i.next();      
            int local_min_occurs        = local_particle.getMinOccurs();
            int local_max_occurs        = local_particle.getMaxOccurs();
            XSDTerm the_term            = local_particle.getTerm(); 
            if (the_term instanceof XSDElementDeclaration)
            {   
                System.out.println("Process xsd term as element "+the_term );
                
                if(group_type==XSDCompositor.CHOICE)
                    buildElementRef((XSDElementDeclaration) the_term,ki_def,group_min_occurs,group_max_occurs);
                else if (group_type==XSDCompositor.SEQUENCE)
                    buildElementRef((XSDElementDeclaration) the_term,ki_def,local_min_occurs,local_max_occurs);
            }
            else if (the_term instanceof XSDModelGroup)
            {
                XSDModelGroup child_group = (XSDModelGroup) the_term;
                System.out.println("Process child model group "+child_group.getCompositor().getName() );
              
                if(model_group.getCompositor().getValue()==XSDCompositor.CHOICE)
                    processModelGroup(child_group, ki_def, group_min_occurs, group_max_occurs);
                else    
                    processModelGroup((XSDModelGroup) the_term, ki_def, local_min_occurs, local_max_occurs);      
            } 
            else if (the_term instanceof XSDWildcard)
            {
                System.out.println("Wildcard "+the_term);
                if(local_min_occurs==0 && local_max_occurs==0)
                    ki_def.setType(TypeDefinition.SIMPLE);
                
                
            }
            else
                System.out.println(the_term);
                
        }
    }
    
    
    private void buildElementRef(XSDElementDeclaration element_dec,TypeDefinition ki_def,int min_occurs,int max_occurs)
    {    
       //if(element_dec.getType()==null) // must be a ref declaration
        
        // it is a ref so it must be an element defined at root level
        // therefore look it up in our original xsd list
        element_dec = xsd_elements.get(element_dec.getTargetNamespace()+element_dec.getName());
       
        ElementDeclaration ki_element   = buildElementDec(element_dec);        
        ElementRef element_ref          = new ElementRef(ki_element,min_occurs,max_occurs);
        ki_def.addElementReference(element_ref);
        System.out.println("---------------");
        System.out.println("Adding element ref "+element_ref);
        System.out.println(" to "+ki_def);
        assessElementType(ki_element,element_dec);       
    }
    
    private void processModelGroupDefinition(XSDModelGroup model_group, TypeDefinition k_int_type_def, int min_occurs, int max_occurs)
    {
        System.out.println("processing model group definition");
        throw new RuntimeException("Should process model group here");
       /** if(model_group.getContainer() instanceof XSDModelGroupDefinitionImpl==false)
            return;
        
        model_group.g
        
        XSDModelGroupDefinitionImpl model_def =  (XSDModelGroupDefinitionImpl) model_group.getContainer();
        
        model_def.get
        
        
        ElementReference ref = new ElementReference();
        ref.name = model_def.getName();
        ref.max_occurs=max_occurs;
        ref.min_occurs=min_occurs;
        ref.type=ref.name;
        k_int_type_def.addElementReference(ref);**/
        
        
        
        //System.out.println(model_def.getModelGroup().getContainer());
        //System.out.println("Process model group definition in type def "+k_int_type_def.getName()+" "+model_def.getName()+" "+min_occurs+" "+max_occurs +" ");
                 
    }
    
    private void analyseEmptyLiteral(XSDComplexTypeDefinition xsd_type,TypeDefinition ki_def)
    {
        System.out.println("Empty literal");
        if(ki_def.getType()==TypeDefinition.SIMPLE)
            analyseSimpleType(xsd_type.getSimpleType(), ki_def);
        else
            analyseMixedOrElement(xsd_type,ki_def);
        
       /** XSDTypeDefinition base_type     = xsd_type.getBaseType();      
        TypeDefinition ki_base_def      =  processTypeDef(base_type);
        System.out.println("Base type "+ki_base_def);
        System.out.println("Real type is "+ki_def);
        
       **/ 
       
        
        
        
       
        // add atributes from base type
        // add patterns from base type
        // add possible values from base type
        
        
        
       
       //System.out.println("Base type "+ki_base_def);
       
       //TypeDefinition ki_def = processTypeDef(xsd_type);
       
        
        
       /** XSDTypeDefinition real_base_type = (XSDTypeDefinition) global_types.get(base_type.getName());
        
        k_int_type_def.setType(TypeDefinition.SIMPLE);
        TypeDefinition k_int_base_type = schema_analysis.getTypeDefinition(base_type.getName());        
        
        //System.out.println("k-int base type is "+k_int_base_type.getName());
        
        if(k_int_base_type==null)
        {
            analyseTypeDef(real_base_type);
            k_int_base_type = schema_analysis.getTypeDefinition(base_type.getName());       
        }
        
        if(k_int_base_type==null)
            return;
    
        Map attributes = k_int_base_type.getAttributeMap();
        
        Iterator i =attributes.keySet().iterator();
        
        while(i.hasNext())
        {
            String name = (String)i.next();
            AttributePair value = (AttributePair)attributes.get(name);
            k_int_type_def.addAttribute(name,value.value,value.type);
        }
                
        i = k_int_base_type.getPossibleValuesIterator();
        while(i.hasNext())
        {
            k_int_type_def.addPossibleValue((String) i.next());
        }
        
        i = k_int_base_type.getElementRefIterator();
        while(i.hasNext())
        {
            ElementReference element_ref = (ElementReference) i.next();
            //System.out.println("Adding element ref "+element_ref.name);
            k_int_type_def.addElementReference(element_ref);
            k_int_type_def.setType(TypeDefinition.COMPLEX);
        }
        
        schema_analysis.addTypeDefinition(k_int_type_def);      
        
        **/
    }
    
    
    private void addAttributeUses(XSDComplexTypeDefinition xsd_def,TypeDefinition ki_def)
    {
        if(xsd_def!=null)
        {       
            Iterator<XSDAttributeUse> attr_uses = xsd_def.getAttributeUses().iterator();
            
            while(attr_uses.hasNext())
            {
                XSDAttributeUse attr_use    = attr_uses.next();     
               
                boolean required            = attr_use.isRequired();
                String value                = (String) attr_use.getValue();
                XSDAttributeDeclaration attr_dec = attr_use.getContent();
               // String name                 = attr_dec.getResolvedAttributeDeclaration().getName();
                String ns                   = attr_dec.getTargetNamespace();
               // System.out.println(attr_dec.getQName());
                //System.out.println("Adding attribute "+ns+name);
                String type=null;
                if(attr_dec.getResolvedAttributeDeclaration().getType()!=null)
                    type=attr_dec.getResolvedAttributeDeclaration().getType().getName();
                             
                ki_def.addAttribute(attr_dec.getQName(),value,type,ns,required);
            }   
        }
    }
    
    
    
    private void analyseSimpleType(XSDSimpleTypeDefinition xsd_type, TypeDefinition ki_def)
    {
        System.out.println("Analyse Simple type "+xsd_type.getTargetNamespace()+xsd_type.getName());
        XSDTypeDefinition root_type = xsd_type.getRootType();
        XSDTypeDefinition base_type = xsd_type.getBaseType();
        //System.out.println("Root type "+root_type);
       // System.out.println("Base type "+base_type);
        XSDTypeDefinition type_to_use = null;
        
        
        if(base_type!=null)
            type_to_use=base_type;
        else if (root_type!=null)
            type_to_use=root_type;
                    
       
        TypeDefinition derived_type = null;
        
        if(type_to_use!=null)
        {
            derived_type = processed_type_defs.get(type_to_use.getTargetNamespace()+type_to_use.getName());       
            if(derived_type==null)
                processTypeDef(type_to_use);
        
            derived_type = processed_type_defs.get(type_to_use.getTargetNamespace()+type_to_use.getName());       
        }
        ki_def.setRootType(derived_type);
     
        // probably need to check for a restriction on this call
        // as per the previous version
        // if restriction we shouldn't be adding the
        // original source params
        if (derived_type!=null) 
        {
            Iterator<ElementRef> refs = derived_type.getElementRefIterator();
            
            while(refs.hasNext())
            {
                ki_def.addElementReference( refs.next());
            }           
            Iterator<AttributeRef> attrs= derived_type.getAttributes().iterator();
            
            while(attrs.hasNext())  
            {
                AttributeRef triple = attrs.next();
                ki_def.addAttribute(triple.getname(),triple.getValue(),triple.getType(),triple.getNamespace(),triple.isRequired());
            }
            
            ki_def.setMaxLength(derived_type.getMaxLength());
            ki_def.setMinLength(derived_type.getMinLength());
            
            Iterator<String> patterns = derived_type.getPatternIterator();
            while(patterns.hasNext())
            {
                ki_def.addPattern(patterns.next());
                
            }
            Iterator<String> values = derived_type.getPossibleValuesIterator();
            while(values.hasNext())
            {
                ki_def.addPossibleValue(values.next());
            }
        }
        // in the previous version there was an  else here which added factes from the 'type_to_use'
        // which 'seemed' redundant..
        addFacetInformation(ki_def,xsd_type);
    }
    
    private void addFacetInformation(TypeDefinition ki_def, XSDSimpleTypeDefinition xsd_type)
    {
        Iterator<XSDEnumerationFacet> i = xsd_type.getEnumerationFacets().iterator();
        
        while(i.hasNext())
        {   
            XSDEnumerationFacet enum_facet = i.next();
            String value = (String) enum_facet.getLexicalValue();               
            ki_def.addPossibleValue(value);
        } 
        if(xsd_type.getEffectiveEnumerationFacet()!=null) // this was commented out in the last version
        {
            Iterator<Object> j = xsd_type.getEffectiveEnumerationFacet().getValue().iterator();
            while(j.hasNext())
            {
                String value = (String) j.next();
                ki_def.addPossibleValue(value);
            }
        }
        Iterator<XSDPatternFacet>k = xsd_type.getPatternFacets().iterator();
        
        while(k.hasNext())
        {              
            String pattern =(i.next()).getLexicalValue();         
            ki_def.addPattern(pattern);
        }   
       
        if(xsd_type.getMaxLengthFacet()!=null)   
            ki_def.setMaxLength(Integer.toString(xsd_type.getMaxLengthFacet().getValue()));
        
        if(xsd_type.getMinLengthFacet()!=null)
            ki_def.setMinLength(Integer.toString(xsd_type.getMinLengthFacet().getValue()));    
        
    }
    
    private String buildAnonTypeDefName(String name,XSDTypeDefinition xsd_type)
    {
        boolean exists  = true;
        name            = name+"AnonType";
        String uri      = xsd_type.getTargetNamespace();
        while(exists==true)
        {
           TypeDefinition def = processed_type_defs.get(uri+name);
           if(def==null)
               exists=false;
           else
               name=name+"Type";
        }       
       return name;
    }
}
