/* * Copyright (C) 2009 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.services.jcr.impl.xml.exporting; import org.apache.ws.commons.util.Base64; import org.exoplatform.services.jcr.dataflow.ItemDataConsumer; import org.exoplatform.services.jcr.dataflow.ItemDataTraversingVisitor; import org.exoplatform.services.jcr.datamodel.InternalQName; import org.exoplatform.services.jcr.datamodel.ItemData; import org.exoplatform.services.jcr.datamodel.NodeData; import org.exoplatform.services.jcr.datamodel.PropertyData; import org.exoplatform.services.jcr.datamodel.QPath; import org.exoplatform.services.jcr.datamodel.ValueData; import org.exoplatform.services.jcr.impl.Constants; import org.exoplatform.services.jcr.impl.core.value.ValueFactoryImpl; import org.exoplatform.services.jcr.impl.dataflow.NodeDataOrderComparator; import org.exoplatform.services.jcr.impl.dataflow.PropertyDataOrderComparator; import org.exoplatform.services.jcr.impl.dataflow.ValueDataUtil; import org.exoplatform.services.jcr.impl.util.ISO9075; import org.xml.sax.SAXException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.jcr.NamespaceRegistry; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.ValueFormatException; import javax.xml.stream.XMLStreamException; /** * Created by The eXo Platform SAS. * * @author <a href="mailto:Sergey.Kabashnyuk@gmail.com">Sergey Kabashnyuk</a> * @version $Id: ImportNodeData.java 11907 2008-03-13 15:36:21Z ksm $ */ public abstract class BaseXmlExporter extends ItemDataTraversingVisitor { /** * Empty namespace prefix. */ public static final String DEFAULT_EMPTY_NAMESPACE_PREFIX = "jcr_default_empty" + "_namespace_prefix"; /** * Multi-value delimiter. */ public static final String MULTI_VALUE_DELIMITER = " "; /** * Root node name. */ protected static final String JCR_ROOT = "jcr:root"; /** * NamespacerRegistry. */ private final NamespaceRegistry namespaceRegistry; /** * If noRecurse is true then only the node at absPath and its properties, but not its child nodes, * are serialized. If noRecurse is false then the entire subtree rooted at absPath is serialized. */ private final boolean noRecurse; /** * Skip binary. */ private final boolean skipBinary; /** * SV namespace uri. */ private final String svNamespaceUri; /** * ValueFactory. */ private final ValueFactoryImpl systemValueFactory; /** * @param dataManager - ItemDataConsumer * @param namespaceRegistry - NamespaceRegistry * @param systemValueFactory - default ValueFactory * @param skipBinary - If skipBinary is true then any properties of PropertyType.BINARY will be * serialized as if they are empty. * @param maxLevel - maximum level * @param noRecurse - noRecurse value * @exception RepositoryException if an repository error occurs. */ public BaseXmlExporter(ItemDataConsumer dataManager, NamespaceRegistry namespaceRegistry, ValueFactoryImpl systemValueFactory, boolean skipBinary, boolean noRecurse, int maxLevel) throws RepositoryException { super(dataManager, maxLevel); this.skipBinary = skipBinary; this.noRecurse = noRecurse; this.namespaceRegistry = namespaceRegistry; this.svNamespaceUri = namespaceRegistry.getURI("sv"); this.systemValueFactory = systemValueFactory; } /** * @param node - exported node. * @throws RepositoryException * @throws SAXException * @throws XMLStreamException */ public abstract void export(NodeData node) throws RepositoryException, SAXException, XMLStreamException; /** * @return - uri of the sv namespace. */ public String getSvNamespaceUri() { return svNamespaceUri; } /** * @return - noRecurse. */ public boolean isNoRecurse() { return noRecurse; } /** * @return - skip binary. */ public boolean isSkipBinary() { return skipBinary; } /** * {@inheritDoc} */ public void visit(NodeData node) throws RepositoryException { try { entering(node, currentLevel); if ((maxLevel == -1) || (currentLevel < maxLevel)) { currentLevel++; List<PropertyData> properies = new ArrayList<PropertyData>(dataManager.getChildPropertiesData(node)); // Sorting properties Collections.sort(properies, new PropertyDataOrderComparator()); for (PropertyData data : properies) { InternalQName propName = data.getQPath().getName(); // 7.3.3 Respecting Property Semantics // When an element or attribute representing such a property is // encountered, an implementation may either skip it or respect it. if (Constants.JCR_LOCKISDEEP.equals(propName) || Constants.JCR_LOCKOWNER.equals(propName)) { continue; } data.accept(this); } if (!isNoRecurse() && (currentLevel > 0)) { List<NodeData> nodes = new ArrayList<NodeData>(dataManager.getChildNodesData(node)); // Sorting nodes Collections.sort(nodes, new NodeDataOrderComparator()); for (NodeData data : nodes) { data.accept(this); } } currentLevel--; } leaving(node, currentLevel); } catch (RepositoryException re) { currentLevel = 0; throw re; } } /** * @param data - exported ItemData. * @param encode - is ISO9075 encode. * @return - exported item name. * @exception RepositoryException if an repository error occurs. */ protected String getExportName(ItemData data, boolean encode) throws RepositoryException { String nodeName; QPath itemPath = data.getQPath(); if (Constants.ROOT_PATH.equals(itemPath)) { nodeName = JCR_ROOT; } else { InternalQName internalNodeName = itemPath.getName(); if (encode) { internalNodeName = ISO9075.encode(itemPath.getName()); } String prefix = namespaceRegistry.getPrefix(internalNodeName.getNamespace()); nodeName = prefix.length() == 0 ? "" : prefix + ":"; if ("".equals(itemPath.getName().getName()) && itemPath.isDescendantOf(Constants.EXO_NAMESPACES_PATH)) { nodeName += DEFAULT_EMPTY_NAMESPACE_PREFIX; //NOSONAR } else { nodeName += internalNodeName.getName(); //NOSONAR } } return nodeName; } /** * @param data - exported value data. * @param type - value type * @return - string representation of values prepared for export. Be attentive method encode * binary values in memory. It is possible OutOfMemoryError on large Values. * @throws IllegalStateException * @throws IOException * @exception RepositoryException if an repository error occurs. * @exception IOException if an I/O error occurs. */ protected String getValueAsStringForExport(ValueData data, int type) throws IOException, RepositoryException { String charValue = null; switch (type) { case PropertyType.BINARY : if (skipBinary) { charValue = ""; } else { charValue = Base64.encode(data.getAsByteArray(), 0, (int)data.getLength(), 0, ""); } break; case PropertyType.NAME : case PropertyType.DATE : case PropertyType.PATH : try { charValue = systemValueFactory.loadValue(data, type).getString(); } catch (ValueFormatException e) { throw new RepositoryException(e); } catch (UnsupportedRepositoryOperationException e) { throw new RepositoryException(e); } break; default : charValue = ValueDataUtil.getString(data); break; } return charValue; } /** * Indicates whether or not the provided String contains only * characters that are valid according to the specification of * XML 1.0 that are defined here http://www.w3.org/TR/xml/#charsets * @param content the content to check * @return <code>true</code> if all the characters of the provided * String are valid regarding the XML specification, <code>false</code> * otherwise */ protected static boolean hasValidCharsOnly(String content) { if (content == null) return true; for (int i = 0, length = content.length(); i < length; i++) { char c = content.charAt(i); if ((c == 0x9) || (c == 0xA) || (c == 0xD) || ((c >= 0x20) && (c <= 0xD7FF)) || ((c >= 0xE000) && (c <= 0xFFFD)) || ((c >= 0x10000) && (c <= 0x10FFFF))) { // The character is valid continue; } // The character is not valid return false; } return true; } public NamespaceRegistry getNamespaceRegistry() { return namespaceRegistry; } public ValueFactoryImpl getSystemValueFactory() { return systemValueFactory; } }