/* * Part of the CCNx Java Library. * * Copyright (C) 2008-2011, 2013 Palo Alto Research Center, Inc. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. You should have received * a copy of the GNU Lesser General Public License along with this library; * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. */ package org.ccnx.ccn.impl.repo; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.impl.CCNFlowControl; import org.ccnx.ccn.impl.CCNFlowControl.SaveType; import org.ccnx.ccn.impl.encoding.CCNProtocolDTags; import org.ccnx.ccn.impl.encoding.GenericXMLEncodable; import org.ccnx.ccn.impl.encoding.XMLDecoder; import org.ccnx.ccn.impl.encoding.XMLEncodable; import org.ccnx.ccn.impl.encoding.XMLEncoder; import org.ccnx.ccn.impl.support.Log; import org.ccnx.ccn.io.ErrorStateException; import org.ccnx.ccn.io.content.CCNEncodableObject; import org.ccnx.ccn.io.content.ContentDecodingException; import org.ccnx.ccn.io.content.ContentEncodingException; import org.ccnx.ccn.io.content.ContentGoneException; import org.ccnx.ccn.io.content.ContentNotReadyException; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.KeyLocator; import org.ccnx.ccn.protocol.MalformedContentNameStringException; import org.ccnx.ccn.protocol.PublisherPublicKeyDigest; /** * Represents repo policy data */ public class PolicyXML extends GenericXMLEncodable implements XMLEncodable { public static class PolicyObject extends CCNEncodableObject<PolicyXML> { /** * Read constructor */ public PolicyObject(ContentName name, CCNHandle handle) throws ContentDecodingException, IOException { super(PolicyXML.class, true, name, (PublisherPublicKeyDigest)null, handle); } /** * Write constructor. */ public PolicyObject(ContentName name, PolicyXML data, SaveType saveType, CCNHandle handle) throws IOException { super(PolicyXML.class, true, name, data, saveType, handle); } /** * Write constructor. */ public PolicyObject(ContentName name, PolicyXML data, PublisherPublicKeyDigest publisher, KeyLocator keyLocator, CCNFlowControl flowControl) throws IOException { super(PolicyXML.class, true, name, data, publisher, keyLocator, flowControl); } public PolicyXML policyInfo() throws ContentNotReadyException, ContentGoneException, ErrorStateException { return data(); } } /** * The following interface and enumeration allow user created policy files with the * data in any order. The encoder goes through the user's names, checks for matches with * the names from the enumeration and encodes the name it sees appropriately */ private interface ElementPutter { public void put(PolicyXML pxml, String value) throws MalformedContentNameStringException; } private enum PolicyElement { VERSION (CCNProtocolDTags.PolicyVersion, new VersionPutter()), NAMESPACE (CCNProtocolDTags.Namespace, new NameSpacePutter()), LOCALNAME (CCNProtocolDTags.LocalName, new LocalNamePutter()), GLOBALPREFIX (CCNProtocolDTags.GlobalPrefix, new GlobalPrefixPutter()); private final int _tagValue; private ElementPutter _putter; PolicyElement(int tagValue, ElementPutter putter) { _tagValue = tagValue; _putter = putter; } public long getTagValue() { return _tagValue; } } private static class VersionPutter implements ElementPutter { public void put(PolicyXML pxml, String value) throws MalformedContentNameStringException { pxml._version = value.trim(); } } private static class GlobalPrefixPutter implements ElementPutter { public void put(PolicyXML pxml, String value) throws MalformedContentNameStringException { pxml.setGlobalPrefix(value.trim()); } } private static class LocalNamePutter implements ElementPutter { public void put(PolicyXML pxml, String value) throws MalformedContentNameStringException { pxml._localName = value.trim(); } } private static class NameSpacePutter implements ElementPutter { public void put(PolicyXML pxml, String value) throws MalformedContentNameStringException { pxml.addNamespace(ContentName.fromNative(value.trim())); } } protected String _version = null; protected ContentName _globalPrefix = null; protected String _localName = null; protected ArrayList<ContentName> _namespace = new ArrayList<ContentName>(); @Override public void decode(XMLDecoder decoder) throws ContentDecodingException { decoder.readStartElement(getElementLabel()); PolicyElement foundElement; do { foundElement = null; // Don't need to back up the stream 4 times for every read... // Probably can improve this yet more given a bit of time. Long startElement = decoder.peekStartElementAsLong(); if (null == startElement) { break; } long startElementVal = startElement.longValue(); for (PolicyElement element : PolicyElement.values()) { if (element.getTagValue() == startElementVal) { foundElement = element; break; } } if (null != foundElement) { String value = decoder.readUTF8Element(foundElement.getTagValue()); try { foundElement._putter.put(this, value); } catch (MalformedContentNameStringException e) { throw new ContentDecodingException(e.getMessage()); } Log.fine(Log.FAC_REPO, "Found policy element {0} with value {1}", foundElement.getTagValue(), value); } } while (null != foundElement); decoder.readEndElement(); } @Override public void encode(XMLEncoder encoder) throws ContentEncodingException { if (!validate()) { throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing."); } encoder.writeStartElement(getElementLabel()); encoder.writeElement(CCNProtocolDTags.PolicyVersion, _version); encoder.writeElement(CCNProtocolDTags.LocalName, _localName); encoder.writeElement(CCNProtocolDTags.GlobalPrefix, _globalPrefix.toString()); if (null != _namespace) { synchronized (_namespace) { for (ContentName name : _namespace) encoder.writeElement(CCNProtocolDTags.Namespace, name.toString()); } } encoder.writeEndElement(); } public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + ((_version == null) ? 0 : _version.hashCode()); result = PRIME * result + ((_localName == null) ? 0 : _localName.hashCode()); result = PRIME * result + ((_globalPrefix == null) ? 0 : _globalPrefix.hashCode()); result = PRIME * result + _namespace.hashCode(); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final PolicyXML other = (PolicyXML) obj; if (!_version.equals(other._version)) return false; if (!_localName.equals(other._localName)) return false; if (!_globalPrefix.equals(other._globalPrefix)) return false; if (_namespace == null) { return (other._namespace == null); } if (other._namespace == null) return false; if (_namespace.size() != other._namespace.size()) return false; ContentName[] ourNamespace = new ContentName[_namespace.size()]; ContentName[] otherNamespace = new ContentName[_namespace.size()]; _namespace.toArray(ourNamespace); other._namespace.toArray(otherNamespace); Arrays.sort(ourNamespace); Arrays.sort(otherNamespace); if (!Arrays.equals(ourNamespace, otherNamespace)) return false; return true; } @Override public long getElementLabel() { return CCNProtocolDTags.Policy; } @Override public boolean validate() { return null != _version; } @SuppressWarnings("unchecked") public synchronized ArrayList<ContentName> getNamespace() { return (ArrayList<ContentName>)_namespace.clone(); } public synchronized void setNamespace(ArrayList<ContentName> namespace) { _namespace = namespace; } public synchronized void addNamespace(ContentName name) { if (null == _namespace) _namespace = new ArrayList<ContentName>(); for (ContentName n : _namespace) { if (n.equals(name)) return; } _namespace.add(name); } public synchronized void removeNamespace(ContentName name) { if (null != _namespace) _namespace.remove(name); } public synchronized void setLocalName(String localName) { _localName = localName; } public synchronized String getLocalName() { return _localName; } public synchronized void setGlobalPrefix(String globalPrefix) throws MalformedContentNameStringException { if (null != _globalPrefix) _namespace.remove(_globalPrefix); _globalPrefix = ContentName.fromNative(fixSlash(globalPrefix)); addNamespace(_globalPrefix); } /** * This is a special case for transferring one policyXML to another (so we already have the * namespace setup correctly). * * @param globalPrefix */ public synchronized void setGlobalPrefixOnly(ContentName globalPrefix) { _globalPrefix = globalPrefix; } public synchronized ContentName getGlobalPrefix() { return _globalPrefix; } public synchronized void setVersion(String version) { _version = version; } public synchronized String getVersion() { return _version; } /** * Global prefix names are not required to start with a slash. Just add one * here if it doesn't * @param name - the test name * @return */ public static String fixSlash(String name) { if (!name.startsWith("/")) name = "/" + name; return name; } }