/******************************************************************************* * 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.runtime.config.validation.verifiers; import static org.hamcrest.Matchers.*; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import org.ebayopensource.turmeric.runtime.config.validation.AbstractVerifier; import org.jdom.Document; import org.jdom.Element; import org.jdom.Namespace; import org.junit.Assert; public class ErrorDataVerifier extends AbstractVerifier { private static final Logger LOG = Logger.getLogger(ErrorDataVerifier.class.getName()); /** * Used to track the ref count of various error names */ static class NameRef { private String name; private int refcount; private List<String> ids = new ArrayList<String>(); public NameRef(String name) { this.name = name; this.refcount = 0; } public void addId(String sid) { this.ids.add(sid); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } NameRef other = (NameRef) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } public List<String> getIds() { return ids; } public String getName() { return name; } public int getRefcount() { return refcount; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } public void increment() { this.refcount++; } } protected void assertNoDuplicateIDs(File hit, Element errorlist) { Namespace ns = errorlist.getNamespace(); @SuppressWarnings("unchecked") List<Element> errorelems = errorlist.getChildren("error", ns); Assert.assertThat("<error> count", errorelems.size(), greaterThanOrEqualTo(1)); // Collect all of the id -> name mappings Map<Integer, List<String>> idMap = new HashMap<Integer, List<String>>(); for (Element elem : errorelems) { String name = elem.getAttributeValue("name"); Assert.assertNotNull("Element has a <null> name", name); String sid = elem.getAttributeValue("id"); Assert.assertNotNull("Element [" + name + "] has a <null> id", sid); int id = Integer.parseInt(sid); List<String> names = idMap.get(id); if (names == null) { names = new ArrayList<String>(); } names.add(name); idMap.put(id, names); } // Validate that there are no duplicate ids. for (int id : idMap.keySet()) { List<String> names = idMap.get(id); if (names.size() > 1) { report.violation("multiple-locations", "Found %d duplicates for id [%d] = names %s", names.size(), id, asArrayString(names)); } } } private void assertNoDuplicateNames(File hit, Element errorlist) { Namespace ns = errorlist.getNamespace(); @SuppressWarnings("unchecked") List<Element> errorelems = errorlist.getChildren("error", ns); Assert.assertThat("<error> count", errorelems.size(), greaterThanOrEqualTo(1)); // Name to Reference Count Map<String, NameRef> namerefs = new HashMap<String, NameRef>(); NameRef ref; for (Element elem : errorelems) { String name = elem.getAttributeValue("name"); Assert.assertNotNull("Element has a <null> name", name); String sid = elem.getAttributeValue("id"); Assert.assertNotNull("Element [" + name + "] has a <null> id", sid); ref = namerefs.get(name); if (ref == null) { ref = new NameRef(name); } ref.increment(); ref.addId(sid); namerefs.put(name, ref); } // Validate that there are no duplicate names. for (NameRef nref : namerefs.values()) { if (nref.getRefcount() > 1) { report.violation("multiple-locations", "Found %d duplicates for name [%s] = ids %s", nref.getRefcount(), nref.getName(), asArrayString(nref.getIds())); } } } @Override public String getFileRegex() { return "META-INF/errorlibrary/([^/]+)/ErrorData.xml"; } @Override public void verifyHit(File hit) { LOG.fine("Verifying: " + hit); try { Document doc = xmlParse(hit); String expectedRootName = "ErrorBundle"; String expectedDefaultNamespace = "http://www.ebayopensource.org/turmeric/common/config"; if (!validRootElement(hit, doc, expectedRootName, expectedDefaultNamespace)) { return; } Element root = doc.getRootElement(); Namespace ns = root.getNamespace(); Matcher matcher = getHitRegexMatcher(hit); // The matcher.find should always be successful, as we wouldn't // be here if it failed to find or match. if (matcher.find()) { String xmlDomain = root.getAttributeValue("domain"); String pathDomain = matcher.group(1); if (!xmlDomain.equals(pathDomain)) { report.violation("//ErrorBundle[@domain]", "Domain name declared in file [%s] must match the directory name in the path to the file [%s] (case is important!)", xmlDomain, pathDomain); } } Element errorlist = root.getChild("errorlist", ns); Assert.assertThat(errorlist, notNullValue()); Assert.assertThat(errorlist.getName(), is("errorlist")); assertNoDuplicateIDs(hit, errorlist); assertNoDuplicateNames(hit, errorlist); } catch (Exception e) { LOG.log(Level.WARNING, "Unable to parse XML: " + hit, e); } } }