/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.apidiff; import com.emc.storageos.apidiff.model.ApiDescriptor; import com.emc.storageos.apidiff.model.ApiDescriptorDiff; import com.emc.storageos.apidiff.model.ApiIdentifier; import com.emc.storageos.apidiff.model.ServiceCatalog; import com.emc.storageos.apidiff.util.Pair; import com.emc.storageos.apidiff.util.XmlDiff; import org.jdom2.Document; import org.jdom2.input.SAXBuilder; import java.io.StringReader; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Records difference of one REST API service between old version and new version, it includes added, * changed and removed APIs. */ public class ServiceCatalogDiff { private final ServiceCatalog oldServiceCatalog; private final ServiceCatalog newServiceCatalog; // The map to store changed api private final Map<ApiIdentifier, ApiDescriptorDiff> apiChangedMap = new HashMap<ApiIdentifier, ApiDescriptorDiff>(); public ServiceCatalogDiff(ServiceCatalog oldServiceCatalog, ServiceCatalog newServiceCatalog) { this.oldServiceCatalog = oldServiceCatalog; this.newServiceCatalog = newServiceCatalog; } public Map<ApiIdentifier, ApiDescriptorDiff> getApiChangedMap() { return apiChangedMap; } public ServiceCatalog getOldServiceCatalog() { return oldServiceCatalog; } public ServiceCatalog getNewServiceCatalog() { return newServiceCatalog; } /** * Computes differences between old repository and new repository */ public void generateDiff() { Map<String, Pair<String, String>> elementChangedMap = new HashMap<String, Pair<String, String>>(); for (Map.Entry<String, String> entry : newServiceCatalog.getElementMap().entrySet()) { Pair<String, String> diff = compareXml(oldServiceCatalog.getElementMap().get(entry.getKey()), entry.getValue()); if (diff != null) { elementChangedMap.put(entry.getKey(), diff); } } Iterator<Map.Entry<ApiIdentifier, ApiDescriptor>> newApiMapIterator = newServiceCatalog.getApiMap().entrySet().iterator(); while (newApiMapIterator.hasNext()) { Map.Entry<ApiIdentifier, ApiDescriptor> entry = newApiMapIterator.next(); ApiDescriptor oldApiResource = oldServiceCatalog.getApiMap().get(entry.getKey()); if (oldApiResource == null) { continue; } Pair<String, String> paramDiff = compareParameter(oldApiResource.getParameters(), entry.getValue().getParameters()); Pair<String, String> requestDiff = elementChangedMap.get(entry.getValue().getRequestElement()); Pair<String, String> responseDiff = elementChangedMap.get(entry.getValue().getResponseElement()); if (paramDiff != null || requestDiff != null || responseDiff != null) { apiChangedMap.put(entry.getKey(), new ApiDescriptorDiff(paramDiff, requestDiff, responseDiff)); } newApiMapIterator.remove(); oldServiceCatalog.getApiMap().remove(entry.getKey()); } oldServiceCatalog.update(); newServiceCatalog.update(); } /** * Compares two parameter list in old resource and new resource * * @param oldParameters * The list of old parameters * @param newParameters * The list of new parameters * @return pair of different parameter string if they are different, else null */ public Pair<String, String> compareParameter(List<String> oldParameters, List<String> newParameters) { Iterator<String> paramIter = newParameters.iterator(); while (paramIter.hasNext()) { String param = paramIter.next(); int index = oldParameters.indexOf(param); if (index != -1) { paramIter.remove(); oldParameters.remove(index); } } if (!oldParameters.isEmpty() || !newParameters.isEmpty()) { return new Pair<String, String>( Arrays.toString(oldParameters.toArray(new String[oldParameters.size()])), Arrays.toString(newParameters.toArray(new String[newParameters.size()]))); } return null; } /** * Compares two string which are well formed XML * * @param oldXml * The old xml string * @param newXml * The new xml string * @return pair of different xml string if they are different, else null */ public Pair<String, String> compareXml(String oldXml, String newXml) { if (oldXml == null && newXml == null) { return null; } if (oldXml == null) { return new Pair<String, String>(null, newXml); } if (newXml == null) { return new Pair<String, String>(oldXml, null); } SAXBuilder saxBuilder = new SAXBuilder(); saxBuilder.setReuseParser(true); Document oldDocument = null, newDocument = null; try { oldDocument = saxBuilder.build(new StringReader(oldXml)); newDocument = saxBuilder.build(new StringReader(newXml)); } catch (Exception ex) { System.err.println("Invalid XML content:\n " + oldXml + "\n and: \n" + newXml); } if (oldDocument == null || newDocument == null) { return null; } return XmlDiff.compareXml(oldDocument, newDocument); } }