package com.mopub.mobileads.util.vast; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; class VastXmlManager { private static final String ROOT_TAG = "MPMoVideoXMLDocRoot"; private static final String ROOT_TAG_OPEN = "<" + ROOT_TAG + ">"; private static final String ROOT_TAG_CLOSE = "</" + ROOT_TAG + ">"; // Element names private static final String IMPRESSION_TRACKER = "Impression"; private static final String VIDEO_TRACKER = "Tracking"; private static final String CLICK_THROUGH = "ClickThrough"; private static final String CLICK_TRACKER = "ClickTracking"; private static final String MEDIA_FILE = "MediaFile"; private static final String VAST_AD_TAG = "VASTAdTagURI"; private static final String MP_IMPRESSION_TRACKER = "MP_TRACKING_URL"; private static final String COMPANION = "Companion"; // Attribute names private static final String EVENT = "event"; private static final String WIDTH = "width"; private static final String HEIGHT = "height"; // Attibute values private static final String START = "start"; private static final String FIRST_QUARTILE = "firstQuartile"; private static final String MIDPOINT = "midpoint"; private static final String THIRD_QUARTILE = "thirdQuartile"; private static final String COMPLETE = "complete"; // This class currently assumes an image type companion ad since that is what we are supporting class ImageCompanionAdXmlManager { // Element name private static final String TRACKING_EVENTS = "TrackingEvents"; private static final String COMPANION_STATIC_RESOURCE = "StaticResource"; private static final String COMPANION_CLICK_THROUGH = "CompanionClickThrough"; // Attribute value private static final String CREATIVE_VIEW = "creativeView"; // Attribute name private static final String CREATIVE_TYPE = "creativeType"; private final Node mCompanionNode; ImageCompanionAdXmlManager(final Node companionNode) throws IllegalArgumentException { if (companionNode == null) { throw new IllegalArgumentException("Companion node cannot be null"); } mCompanionNode = companionNode; } Integer getWidth() { return XmlUtils.getAttributeValueAsInt(mCompanionNode, WIDTH); } Integer getHeight() { return XmlUtils.getAttributeValueAsInt(mCompanionNode, HEIGHT); } String getType() { final Node node = XmlUtils.getFirstMatchingChildNode( mCompanionNode, COMPANION_STATIC_RESOURCE ); return XmlUtils.getAttributeValue(node, CREATIVE_TYPE); } String getImageUrl() { final Node node = XmlUtils.getFirstMatchingChildNode( mCompanionNode, COMPANION_STATIC_RESOURCE ); return XmlUtils.getNodeValue(node); } String getClickThroughUrl() { final Node node = XmlUtils.getFirstMatchingChildNode( mCompanionNode, COMPANION_CLICK_THROUGH ); return XmlUtils.getNodeValue(node); } List<String> getClickTrackers() { final List<String> companionAdClickTrackers = new ArrayList<String>(); final Node node = XmlUtils.getFirstMatchingChildNode( mCompanionNode, TRACKING_EVENTS ); if (node == null) { return companionAdClickTrackers; } final List<Node> trackerNodes = XmlUtils.getMatchingChildNodes( node, VIDEO_TRACKER, EVENT, Arrays.asList(CREATIVE_VIEW) ); for (final Node trackerNode : trackerNodes) { if (trackerNode.getFirstChild() != null) { companionAdClickTrackers.add(trackerNode.getFirstChild().getNodeValue().trim()); } } return companionAdClickTrackers; } } class MediaXmlManager { // Attribute names private static final String DELIVERY = "delivery"; private static final String VIDEO_TYPE = "type"; private final Node mMediaNode; MediaXmlManager(final Node mediaNode) throws IllegalArgumentException { if (mediaNode == null) { throw new IllegalArgumentException("Media node cannot be null"); } mMediaNode = mediaNode; } String getDelivery() { return XmlUtils.getAttributeValue(mMediaNode, DELIVERY); } Integer getWidth() { return XmlUtils.getAttributeValueAsInt(mMediaNode, WIDTH); } Integer getHeight() { return XmlUtils.getAttributeValueAsInt(mMediaNode, HEIGHT); } String getType() { return XmlUtils.getAttributeValue(mMediaNode, VIDEO_TYPE); } String getMediaUrl() { return XmlUtils.getNodeValue(mMediaNode); } } private Document mVastDoc; void parseVastXml(String xmlString) throws ParserConfigurationException, IOException, SAXException { // if the xml string starts with <?xml?>, this tag can break parsing if it isn't formatted exactly right // or if it's not the first line of the document...we're just going to strip it xmlString = xmlString.replaceFirst("<\\?.*\\?>", ""); // adserver may embed additional impression trackers as a sibling node of <VAST> // wrap entire document in root node for this case. String documentString = ROOT_TAG_OPEN + xmlString + ROOT_TAG_CLOSE; DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setCoalescing(true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); mVastDoc = documentBuilder.parse(new InputSource(new StringReader(documentString))); } String getVastAdTagURI() { List<String> uriWrapper = XmlUtils.getStringDataAsList(mVastDoc, VAST_AD_TAG); return (uriWrapper.size() > 0) ? uriWrapper.get(0) : null; } List<String> getImpressionTrackers() { List<String> impressionTrackers = XmlUtils.getStringDataAsList(mVastDoc, IMPRESSION_TRACKER); impressionTrackers.addAll(XmlUtils.getStringDataAsList(mVastDoc, MP_IMPRESSION_TRACKER)); return impressionTrackers; } List<String> getVideoStartTrackers() { return getVideoTrackerByAttribute(START); } List<String> getVideoFirstQuartileTrackers() { return getVideoTrackerByAttribute(FIRST_QUARTILE); } List<String> getVideoMidpointTrackers() { return getVideoTrackerByAttribute(MIDPOINT); } List<String> getVideoThirdQuartileTrackers() { return getVideoTrackerByAttribute(THIRD_QUARTILE); } List<String> getVideoCompleteTrackers() { return getVideoTrackerByAttribute(COMPLETE); } String getClickThroughUrl() { List<String> clickUrlWrapper = XmlUtils.getStringDataAsList(mVastDoc, CLICK_THROUGH); return (clickUrlWrapper.size() > 0) ? clickUrlWrapper.get(0) : null; } List<String> getClickTrackers() { return XmlUtils.getStringDataAsList(mVastDoc, CLICK_TRACKER); } String getMediaFileUrl() { List<String> urlWrapper = XmlUtils.getStringDataAsList(mVastDoc, MEDIA_FILE); return (urlWrapper.size() > 0) ? urlWrapper.get(0) : null; } List<MediaXmlManager> getMediaXmlManagers() { final NodeList nodes = mVastDoc.getElementsByTagName(MEDIA_FILE); final List<MediaXmlManager> mediaXmlManagers = new ArrayList<MediaXmlManager>(nodes.getLength()); for (int i = 0; i < nodes.getLength(); ++i) { mediaXmlManagers.add(new MediaXmlManager(nodes.item(i))); } return mediaXmlManagers; } List<ImageCompanionAdXmlManager> getCompanionAdXmlManagers() { final NodeList nodes = mVastDoc.getElementsByTagName(COMPANION); final List<ImageCompanionAdXmlManager> imageCompanionAdXmlManagers = new ArrayList<ImageCompanionAdXmlManager>(nodes.getLength()); for (int i = 0; i < nodes.getLength(); ++i) { imageCompanionAdXmlManagers.add(new ImageCompanionAdXmlManager(nodes.item(i))); } return imageCompanionAdXmlManagers; } private List<String> getVideoTrackerByAttribute(final String attributeValue) { return XmlUtils.getStringDataAsList(mVastDoc, VIDEO_TRACKER, EVENT, attributeValue); } }