/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* 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
*******************************************************************************/
/**
*
*/
package org.ebayopensource.turmeric.tools.library;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import javax.xml.bind.JAXB;
import javax.xml.namespace.QName;
import org.ebayopensource.turmeric.runtime.common.impl.utils.CallTrackingLogger;
import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenUtil;
import org.ebayopensource.turmeric.common.config.LibraryType;
import org.ebayopensource.turmeric.common.config.ReferredType;
import org.ebayopensource.turmeric.common.config.ReferredTypeLibraryType;
import org.ebayopensource.turmeric.common.config.TypeDependencyType;
import org.ebayopensource.turmeric.common.config.TypeInformationType;
import org.ebayopensource.turmeric.common.config.TypeLibraryDependencyType;
import org.ebayopensource.turmeric.common.config.TypeLibraryType;
/**
* @author arajmony
*
*/
public class TypeDependencyParser {
private static TypeDependencyParser typeDependencyParser;
/*
* performance related variables
*/
private static int typeTableInitialVectorSize = 2;
private Map<LibraryTypeWrapper,Set<LibraryTypeWrapper>> refToTypeDepChildToParentMap;
private Map<LibraryTypeWrapper,Set<LibraryTypeWrapper>> refToTypeDepParentToChildMap;
private Map<QName,LibraryTypeWrapper> refToMasterTypeInfoTable;
private Map<String,TypeLibraryType> refToGlobalLibraryMap;
private static final String TYPE_DEPENDENCY_FILE_NAME = "TypeDependencies.xml";
private static CallTrackingLogger logger = LogManager.getInstance(TypeDependencyParser.class);
private static CallTrackingLogger getLogger(){
return logger;
}
/**
*
* @return An instance of TypeDependencyParser
*/
public static synchronized TypeDependencyParser getInstance() {
if (typeDependencyParser == null) {
typeDependencyParser = new TypeDependencyParser();
}
return typeDependencyParser;
}
/**
*
* @param typeLibraryName
*/
public void processTypeDepXMLFile(String typeLibraryName) throws Exception{
refToMasterTypeInfoTable = SOAGlobalRegistryImpl.getInstance().getMastertypeInfoTable();
refToTypeDepParentToChildMap = SOAGlobalRegistryImpl.getInstance().getParentToChildMap();
refToTypeDepChildToParentMap = SOAGlobalRegistryImpl.getInstance().getChildToParentMap();
String typeDependenciesFilePath = TypeLibraryConstants.META_INF_FOLDER + "/" + typeLibraryName + "/" + TYPE_DEPENDENCY_FILE_NAME;
ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader();
InputStream inStream = null;
TypeLibraryDependencyType typeLibraryDependencyType = null;
//TODO : log a info message here
try{
inStream = myClassLoader.getResourceAsStream(typeDependenciesFilePath);
if(inStream == null) return; // A project may not have the TypeDependency.xml file and it is valid
typeLibraryDependencyType = JAXB.unmarshal(inStream,TypeLibraryDependencyType.class );
}catch(Exception e){
getLogger().log(Level.SEVERE, "Unable to parse the TypeDepedencies.xml file, of library " + typeLibraryName + " its content could be invalid", e);
throw e;
}finally{
CodeGenUtil.closeQuietly(inStream);
}
processTypeLibraryDependencyType(typeLibraryDependencyType);
}
/* A sample TypeDependencies.xml file for understanding the code a little better
*
* <typeLibraryDependencyType libraryName="HardwareLibrary" version="1.0.0" xmlns="http://www.ebayopensource.org/turmeric/common/config">
* <type name="hardwareutilitiesType" version="1.0.0">
* <referredTypeLibrary name="HardwareLibrary" version="1.0.0">
* <referredType name="hardwareType" version="1.0.0"/>
* <referredType name="writerdetailsType" version="1.0.0"/>
* <referredType name="powerdetailsType" version="1.0.0"/>
* <referredType name="monitordetailsType" version="1.0.0"/>
* </referredTypeLibrary>
* </type>
*/
/**
*
* @param typeLibraryDependencyType
*/
public void processTypeLibraryDependencyType(TypeLibraryDependencyType typeLibraryDependencyType) throws Exception{
refToGlobalLibraryMap = SOAGlobalRegistryImpl.getInstance().getGlobalLibraryMap();
List<TypeDependencyType> listTypeDependencyType = typeLibraryDependencyType.getType();
String currLibraryName = typeLibraryDependencyType.getLibraryName();
String nameSpaceCurrLibrary = refToGlobalLibraryMap.get(currLibraryName).getLibraryNamespace();
/*
* Before procssing the current TypeLibraryDependencyType , try and get the details of the types of the corresponding type library.
* This is needed for removing the dependency from the global tables for the following scenario
*
* 1. TypeA currently refers only to TypeB. Therefore TypeDependencies.xml has an entry for this.
* 2. user modifies TypeA to remove the dependency with TypeB. Now since TypeA is not dependent on any Type there would be
* no entry for TypeA in the TypeDependencies.xml any more. i.e in such cases the input param to this method TypeLibraryDependencyType
* will not have this piece of deleted information.
*/
Map<LibraryType, List<LibraryType>> mapOfTypeAndItsParentsAsPerTheOldTypeDependencies = getTypesAndTheirDirectParents(currLibraryName);
/*
* process each of the TypeDependencyType
*/
List<LibraryType> listOfProcessedLibraryTypes = new ArrayList<LibraryType>();
Iterator<TypeDependencyType> iterator = listTypeDependencyType.iterator();
//processing element <type ...>
while(iterator.hasNext()){
TypeDependencyType currTypeDependencyType = iterator.next();
listOfProcessedLibraryTypes.add(getLibraryTypeFromTypeDependencyType(currTypeDependencyType,nameSpaceCurrLibrary));
processTypeDependencyType(currTypeDependencyType,currLibraryName);
}
processAnyDeletedDependencies(
mapOfTypeAndItsParentsAsPerTheOldTypeDependencies,
listOfProcessedLibraryTypes);
}
private Map<LibraryType, List<LibraryType>> getTypesAndTheirDirectParents(String currLibraryName) {
refToGlobalLibraryMap = SOAGlobalRegistryImpl.getInstance().getGlobalLibraryMap();
Map<LibraryType,List<LibraryType>> mapOfTypeAndItsParents = new HashMap<LibraryType, List<LibraryType>>();
try{
SOATypeRegistry typeRegistry = SOAGlobalRegistryFactory.getSOATypeRegistryInstance();
TypeLibraryType typeLibraryType = typeRegistry.getTypeLibrary(currLibraryName);
String nameSpaceCurrLibrary = refToGlobalLibraryMap.get(currLibraryName).getLibraryNamespace();
for(TypeInformationType typeInformationType : typeLibraryType.getType()){
LibraryType libraryType = getLibraryTypeFromTypeInformationType(typeInformationType,nameSpaceCurrLibrary);
List<LibraryType> listOfParents = typeRegistry.getDependentParentTypeFiles(libraryType, 1);
if(listOfParents.size() > 0)
mapOfTypeAndItsParents.put(libraryType, listOfParents);
}
}catch(Exception e){
getLogger().log(Level.INFO, "Exception while trying to get parents for each of the types in the library : " + currLibraryName);
}
return mapOfTypeAndItsParents;
}
private void processAnyDeletedDependencies(Map<LibraryType, List<LibraryType>> oldMappingOfTypeAndItsParents, List<LibraryType> listOfProcessedLibraryTypes) {
refToMasterTypeInfoTable = SOAGlobalRegistryImpl.getInstance().getMastertypeInfoTable();
refToTypeDepParentToChildMap = SOAGlobalRegistryImpl.getInstance().getParentToChildMap();
refToTypeDepChildToParentMap = SOAGlobalRegistryImpl.getInstance().getChildToParentMap();
// converting LibraryTypes to LibraryTypeWrapper's since we are going to use removeAll method and onnly the wrapper class has an
// over-ridden equals method . LibraryType class is a generated one and hence we cannot over-ride equals method
Set<LibraryType> setOfTypesInOldDependenciesFile = oldMappingOfTypeAndItsParents.keySet();
Set<LibraryTypeWrapper> setOfWrapperTypesInOldDependenciesFile = new HashSet<LibraryTypeWrapper>();
for(LibraryType libraryType: setOfTypesInOldDependenciesFile)
setOfWrapperTypesInOldDependenciesFile.add(new LibraryTypeWrapper(libraryType));
List<LibraryTypeWrapper> listOfProcessedLibraryTypeWrappers = new ArrayList<LibraryTypeWrapper>();
for(LibraryType libraryType: listOfProcessedLibraryTypes)
listOfProcessedLibraryTypeWrappers.add(new LibraryTypeWrapper(libraryType));
setOfWrapperTypesInOldDependenciesFile.removeAll(listOfProcessedLibraryTypeWrappers);
// Now setOfTypesInOldDependenciesFile contains only those Types for which dependency existed earlier but has now been removed totally from the
// TypeDependencies.xml . For each such type in the set
// a) get the list of the old parents and for each of the parent types remove the current type as one of its childs. (refToTypeDepParentToChildMap)
// b) since the current type does not have any parents any more , remove the current type from refToTypeDepChildToParentMap
Iterator<LibraryTypeWrapper> libraryTypeIter = setOfWrapperTypesInOldDependenciesFile.iterator();
while(libraryTypeIter.hasNext()){
LibraryTypeWrapper currLibraryTypeWrapper = libraryTypeIter.next();
LibraryType currLibraryType = currLibraryTypeWrapper.getLibraryType();
try {
for(LibraryType parentLibraryType : oldMappingOfTypeAndItsParents.get(currLibraryType) ){
LibraryTypeWrapper parenTypeWrapper = new LibraryTypeWrapper(parentLibraryType);
Set<LibraryTypeWrapper> listOfChilds = refToTypeDepParentToChildMap.get(parenTypeWrapper);
if(listOfChilds != null){
listOfChilds.remove(currLibraryTypeWrapper);
refToTypeDepParentToChildMap.put(parenTypeWrapper, listOfChilds);
}
}
refToTypeDepChildToParentMap.remove(currLibraryTypeWrapper);
} catch (Exception e) {
getLogger().log(Level.INFO, "Exception while trying to get dependent childs for type : "+ currLibraryType.getName());
}
}
}
private LibraryType getLibraryTypeFromTypeInformationType(TypeInformationType typeInformationType, String nameSpaceCurrLibrary) {
LibraryType libraryType = new LibraryType();
libraryType.setName(typeInformationType.getXmlTypeName());
libraryType.setVersion(typeInformationType.getVersion());
libraryType.setNamespace(nameSpaceCurrLibrary);
return libraryType;
}
private LibraryType getLibraryTypeFromTypeDependencyType(TypeDependencyType currTypeDependencyType, String nameSpaceCurrLibrary) {
LibraryType libraryType = new LibraryType();
libraryType.setName(currTypeDependencyType.getName());
libraryType.setVersion(currTypeDependencyType.getVersion());
libraryType.setNamespace(nameSpaceCurrLibrary);
return libraryType;
}
/**
*
* @param currTypeDependencyType
* @param libraryName
*/
public void processTypeDependencyType(TypeDependencyType currTypeDependencyType, String masterLibraryName) throws Exception{
refToMasterTypeInfoTable = SOAGlobalRegistryImpl.getInstance().getMastertypeInfoTable();
refToTypeDepParentToChildMap = SOAGlobalRegistryImpl.getInstance().getParentToChildMap();
refToTypeDepChildToParentMap = SOAGlobalRegistryImpl.getInstance().getChildToParentMap();
refToGlobalLibraryMap = SOAGlobalRegistryImpl.getInstance().getGlobalLibraryMap();
SOATypeRegistry typeRegistry = SOAGlobalRegistryFactory.getSOATypeRegistryInstance();
String currentTypeName = currTypeDependencyType.getName();
String currTypesNS = refToGlobalLibraryMap.get(masterLibraryName).getLibraryNamespace();
QName currTypesQName = new QName(currTypesNS,currentTypeName);
LibraryTypeWrapper currLibraryType = refToMasterTypeInfoTable.get(currTypesQName);
/* The reason for this fix is debatable (return if currLibraryType is null) , this can be null only in the folowing scenario
* If a type is mentioned in a TypeDependency.xml of a library but that type's information is missing in the TypeInformation.xml of the library
* , which indicates that their is a mis-match which can't exist. But plugin is throwing an error and hence fixing it
*/
if(currLibraryType == null)
return;
Set<LibraryTypeWrapper> referredTypeHolder = new HashSet<LibraryTypeWrapper>(typeTableInitialVectorSize);
List<LibraryType> listOfExistingParentTypes = typeRegistry.getDependentParentTypeFiles(currLibraryType.getLibraryType(), 1);
List<ReferredTypeLibraryType> listOfReferredTypeLibrary = currTypeDependencyType.getReferredTypeLibrary();
Iterator<ReferredTypeLibraryType> iteratorRefTLT = listOfReferredTypeLibrary.iterator();
//processing element <referredTypeLibrary ....>
while(iteratorRefTLT.hasNext()){
ReferredTypeLibraryType currReferredTypeLibraryType = iteratorRefTLT.next();
String currReferredLibraryName = currReferredTypeLibraryType.getName();
String currReferredLibraryNS = refToGlobalLibraryMap.get(currReferredLibraryName).getLibraryNamespace();
List<ReferredType> listOfReferredtype = currReferredTypeLibraryType.getReferredType();
Iterator<ReferredType> iteratorRefType = listOfReferredtype.iterator();
//procesing element <referredType ...>
while(iteratorRefType.hasNext()){
ReferredType currReferredType = iteratorRefType.next();
String referredTypeName = currReferredType.getName();
QName referredTypesQName = new QName(currReferredLibraryNS,referredTypeName);
LibraryTypeWrapper referredType = refToMasterTypeInfoTable.get(referredTypesQName);
if(referredType == null){
// This can be null, if
// a) The corresponding library is yet to be processed/updated or/and
// b) The library is processed but there is no valid type like this
// Try populating the referredLibrary once more, probably its updated
try{
TypeInformationParser informationParser = TypeInformationParser.getInstance();
informationParser.processTypeInfoXMLFile(currReferredLibraryName);
}catch(Exception e){
String msg = "As per the TypeDependencies.xml file for the library : "+ masterLibraryName +
" , the type : " + currentTypeName + " refers to the type : " + referredTypesQName + " of the library :" +
currReferredLibraryName + " . But this referred library is not in the global tables and it is not yet loaded";
getLogger().log(Level.SEVERE,msg,e );
throw e;
}
referredType = refToMasterTypeInfoTable.get(referredTypesQName);
if(referredType == null){
/* if it is still null , what to do ? ()i.e The referred library is already there in the global tables
* but the corresponding TypeInformation.xml does not have the details of this yet
* a) Should we throw an error ? or
* b) just create a type for the same and update the information in the global TypeInfo table
*
* For the time being I am following logic (a)
*/
String msg = "As per the TypeDependencies.xml file for the library : "+ masterLibraryName +
" , the type : " + currentTypeName + " refers to the type : " + referredTypesQName + " of the library :" +
currReferredLibraryName + " . But the TypeInformation.xml of the referred library file does not have information" +
" about this type. pls take corrective action.";
getLogger().log(Level.SEVERE, msg);
throw new Exception(msg);
/* logic for (b)
refToGlobalLibraryMap = SOAGlobalRegistryImpl.getInstance().getGlobalLibraryMap();
LibraryType libraryType = new LibraryType();
libraryType.setName(referredTypeName);
libraryType.setVersion(TypeLibraryConstants.TYPE_DEFAULT_VERSION);
libraryType.setLibraryInfo(refToGlobalLibraryMap.get(currReferredLibraryName));
referredType = new LibraryTypeWrapper(libraryType);
refToMasterTypeInfoTable.put(referredTypeName, referredType);
*/
}
}
// { child to parent
referredTypeHolder.add(referredType);
refToTypeDepChildToParentMap.put(currLibraryType, referredTypeHolder);
// } child to parent
// { parent to child
Set<LibraryTypeWrapper> setLibTyp = null;
boolean instanceExists = refToTypeDepParentToChildMap.containsKey(referredType);
if(instanceExists){
setLibTyp = refToTypeDepParentToChildMap.get(referredType);
if(setLibTyp == null) // this condition can never be true ! TODO
setLibTyp = new HashSet<LibraryTypeWrapper>(typeTableInitialVectorSize);
setLibTyp.add(currLibraryType);
}
else {
setLibTyp = new HashSet<LibraryTypeWrapper>(4);
setLibTyp.add(currLibraryType);
}
refToTypeDepParentToChildMap.put(referredType,setLibTyp);
// } parent to child
}
}
/*
* The following piece of code takes care of the situation where a type dependency is removed for a type
* 1. TypeA currently refers to TypeB and TypeC
* 2. user modifies TypeA to remove the dependency with TypeC i.e TypeA no longer depends on TypeC
* 3. So TypeC does not have TypeA as its child an more and this has to be reflected in the global tables.
* 4. But since TypeA is still dependent on TypeB and therefore an entry for the same would be there in the
* TypeDependencies.xml file.
*
*
*/
if(listOfExistingParentTypes.size() != referredTypeHolder.size()){
Vector<LibraryTypeWrapper> existingParentTypeWrappers = new Vector<LibraryTypeWrapper>(listOfExistingParentTypes.size());
for(LibraryType libraryType : listOfExistingParentTypes)
existingParentTypeWrappers.add(new LibraryTypeWrapper(libraryType));
existingParentTypeWrappers.removeAll(referredTypeHolder);
for(LibraryTypeWrapper libraryTypeWrapper : existingParentTypeWrappers){
Set<LibraryTypeWrapper> listOfParents = refToTypeDepParentToChildMap.get(libraryTypeWrapper);
if(listOfParents != null){
listOfParents.remove(currLibraryType);
refToTypeDepParentToChildMap.put(libraryTypeWrapper, listOfParents);
}
}
}
}
}