/* * 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.core.nodetype.registration; import org.exoplatform.services.jcr.core.ExtendedPropertyType; import org.exoplatform.services.jcr.core.nodetype.NodeDefinitionData; import org.exoplatform.services.jcr.core.nodetype.NodeTypeData; import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionData; import org.exoplatform.services.jcr.datamodel.InternalQName; import org.exoplatform.services.jcr.impl.Constants; import org.exoplatform.services.jcr.impl.core.LocationFactory; import org.exoplatform.services.jcr.impl.core.NamespaceRegistryImpl; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.jcr.RepositoryException; import javax.jcr.version.OnParentVersionAction; /** * Created by The eXo Platform SAS.<br> * Class provides CND grammar manipulation tool for writing list of node types * and concomitant namespaces to stream in CDN text format. * * @author <a href="mailto:nikolazius@gmail.com">Nikolay Zamosenchuk</a> * @version $Id: $ */ public class CNDStreamWriter { /** * RegistryImpl instance */ private final NamespaceRegistryImpl namespaceRegistry; /** * Location factory instance */ private final LocationFactory locationFactory; /** * Constructs instance of CNDStreamWriter. Instance of NamespaceRegistryImpl * is used to convert InternalQnames to strings. * * @param namespaceRegistry */ public CNDStreamWriter(NamespaceRegistryImpl namespaceRegistry) { this.namespaceRegistry = namespaceRegistry; this.locationFactory = new LocationFactory(namespaceRegistry); } /** * Write given list of node types to output stream. * * @param nodeTypes * List of NodeTypes to write. * @param os * OutputStream to write to. * @throws RepositoryException */ public void write(List<NodeTypeData> nodeTypes, OutputStream os) throws RepositoryException { OutputStreamWriter out = new OutputStreamWriter(os); try { for (NodeTypeData nodeType : nodeTypes) { printNamespaces(nodeType, out); printNodeTypeDeclaration(nodeType, out); } out.close(); } catch (IOException e) { throw new RepositoryException(e.getMessage(), e); } } /** * Print namespaces to stream * * @param nodeTypeData * @param out * @throws RepositoryException * @throws IOException */ private void printNamespaces(NodeTypeData nodeTypeData, OutputStreamWriter out) throws RepositoryException, IOException { /** * Using set to store all prefixes found in node types to avoid * duplication */ Set<String> namespaces = new HashSet<String>(); /** Scanning nodeType definition for used namespaces */ printNameNamespace(nodeTypeData.getName(), namespaces); printNameNamespace(nodeTypeData.getPrimaryItemName(), namespaces); if (nodeTypeData.getDeclaredSupertypeNames() != null) { for (InternalQName reqType : nodeTypeData.getDeclaredSupertypeNames()) { printNameNamespace(reqType, namespaces); } } /** Scanning property definitions for used namespaces */ if (nodeTypeData.getDeclaredPropertyDefinitions() != null) { for (PropertyDefinitionData property : nodeTypeData.getDeclaredPropertyDefinitions()) { printNameNamespace(property.getName(), namespaces); } } /** Scanning child definitions for used namespaces */ if (nodeTypeData.getDeclaredChildNodeDefinitions() != null) { for (NodeDefinitionData child : nodeTypeData.getDeclaredChildNodeDefinitions()) { printNameNamespace(child.getName(), namespaces); printNameNamespace(child.getDefaultPrimaryType(), namespaces); if (child.getRequiredPrimaryTypes() != null) { for (InternalQName reqType : child.getRequiredPrimaryTypes()) { printNameNamespace(reqType, namespaces); } } } } for (String prefix : namespaces) { String uri = namespaceRegistry.getURI(prefix); out.write("<" + prefix + "='" + uri + "'>\r\n"); } } private void printNameNamespace(InternalQName name, Set<String> namespaces) throws RepositoryException { /** Adding current name's prefix to set if it is not default */ if (name != null) { String prefix = locationFactory.createJCRName(name).getPrefix(); if (!namespaceRegistry.isDefaultPrefix(prefix)) { namespaces.add(prefix); } } } /** * Method recursively print to output stream node type definition in cnd * format. * * @param nodeTypeData * is nodeType to print * @throws RepositoryException * this exception may be while converting QNames to string * @throws IOException * this exception my be thrown during some operations with streams */ private void printNodeTypeDeclaration(NodeTypeData nodeTypeData, OutputStreamWriter out) throws RepositoryException, IOException { /** Print name */ out.write("[" + qNameToString(nodeTypeData.getName()) + "] "); /** Print supertypes */ InternalQName[] superTypes = nodeTypeData.getDeclaredSupertypeNames(); if (superTypes != null && superTypes.length > 0) { /** if there is only 1 element and it is NT_BASE then avoid printing it */ if (superTypes.length > 1 || !superTypes[0].equals(Constants.NT_BASE)) { out.write("> " + qNameToString(superTypes[0])); for (int i = 1; i < superTypes.length; i++) { out.write(", " + qNameToString(superTypes[i])); } } } /** Print attributes */ StringBuilder attributes = new StringBuilder(); // if (nodeTypeData.isAbstract()) // { // attributes += "abstract "; // } if (nodeTypeData.hasOrderableChildNodes()) { attributes.append("orderable "); } if (nodeTypeData.isMixin()) { attributes.append("mixin "); } // if (!nodeTypeData.isQueryable()) // { // attributes += "noquery "; // } if (nodeTypeData.getPrimaryItemName() != null) { attributes.append("primaryitem " + qNameToString(nodeTypeData.getPrimaryItemName())); } if (attributes.length() > 0) { out.write("\r\n "); out.write(attributes.toString()); } /** Print all property definitions */ PropertyDefinitionData[] propertyDefinitions = nodeTypeData.getDeclaredPropertyDefinitions(); if (propertyDefinitions != null) { for (PropertyDefinitionData propertyDefinition : propertyDefinitions) { printPropertyDeclaration(propertyDefinition, out); } } /** Print all child definitions */ NodeDefinitionData[] nodeDefinitions = nodeTypeData.getDeclaredChildNodeDefinitions(); if (nodeDefinitions != null) { for (NodeDefinitionData nodeDefinition : nodeDefinitions) { printChildDeclaration(nodeDefinition, out); } } out.write("\r\n"); } /** * Prints to output stream property definition in CND format * * @param propertyDefinition * @throws IOException * @throws RepositoryException */ private void printPropertyDeclaration(PropertyDefinitionData propertyDefinition, OutputStreamWriter out) throws IOException, RepositoryException { /** Print name */ out.write("\r\n "); out.write("- " + qNameToString(propertyDefinition.getName())); out.write(" (" + ExtendedPropertyType.nameFromValue(propertyDefinition.getRequiredType()).toUpperCase() + ")"); /** Print default values */ out.write(listToString(propertyDefinition.getDefaultValues(), "'", "\r\n = ", " ")); /** Print attributes */ StringBuilder attributes = new StringBuilder(); if (propertyDefinition.isAutoCreated()) { attributes.append("autocreated "); } if (propertyDefinition.isMandatory()) { attributes.append("mandatory "); } if (propertyDefinition.isProtected()) { attributes.append("protected "); } if (propertyDefinition.isMultiple()) { attributes.append("multiple "); } // if (!propertyDefinition.isFullTextSearchable()) // { // attributes += "nofulltext "; // } // if (!propertyDefinition.isQueryOrderable()) // { // attributes += "noqueryorder "; // } // /** Print operators avoiding printing all */ // String[] opArray = propertyDefinition.getAvailableQueryOperators(); // /** Using set to avoid duplication */ // Set<String> opSet = new HashSet<String>(); // for (String op : opArray) // { // opSet.add(op.toUpperCase()); // } // /** if not all elements are mentioned in list then print */ // if (opSet.size() < 7 && opSet.size() > 0) // { // String opString = opSet.toString(); // // opString="queryops '"+opString.substring(1, opString.length()-1)+"' "; // opString = listToString(opSet.toArray(new String[opSet.size()]), "", "\r\n queryops '", "' "); // attributes += opString; // } if (propertyDefinition.getOnParentVersion() != OnParentVersionAction.COPY) { attributes.append("\r\n " + OnParentVersionAction.nameFromValue(propertyDefinition.getOnParentVersion()) + " "); } /** Don't print if all attributes are default */ if (attributes.length() > 0) { out.write("\r\n "); out.write(attributes.toString()); } out.write(listToString(propertyDefinition.getValueConstraints(), "'", "\r\n < ", " ")); } /** * Print to output stream child node definition in CND format * * @param nodeDefinition * @throws IOException * @throws RepositoryException */ private void printChildDeclaration(NodeDefinitionData nodeDefinition, OutputStreamWriter out) throws IOException, RepositoryException { out.write("\r\n "); out.write("+ " + qNameToString(nodeDefinition.getName()) + " "); InternalQName[] requiredTypes = nodeDefinition.getRequiredPrimaryTypes(); if (requiredTypes != null && requiredTypes.length > 0) { /** if there is only 1 element and it is NT_BASE then avoid printing it */ if (requiredTypes.length > 1 || !requiredTypes[0].equals(Constants.NT_BASE)) { out.write("(" + qNameToString(requiredTypes[0])); for (int i = 1; i < requiredTypes.length; i++) { out.write(", " + qNameToString(requiredTypes[i])); } out.write(")"); } } if (nodeDefinition.getDefaultPrimaryType() != null) { out.write("\r\n = " + qNameToString(nodeDefinition.getDefaultPrimaryType())); } StringBuilder attributes = new StringBuilder(); if (nodeDefinition.isAutoCreated()) { attributes.append("autocreated "); } if (nodeDefinition.isMandatory()) { attributes.append("mandatory "); } if (nodeDefinition.isProtected()) { attributes.append("protected "); } if (nodeDefinition.isAllowsSameNameSiblings()) { attributes.append("sns "); } if (nodeDefinition.getOnParentVersion() != OnParentVersionAction.COPY) { attributes.append("\r\n " + OnParentVersionAction.nameFromValue(nodeDefinition.getOnParentVersion()) + " "); } if (attributes.length() > 0) { out.write("\r\n "); out.write(attributes.toString()); } } /** * Converts String[] to String using given notation: * "beforeString+quote+element+quote+', '+quote+element...+quote+afterString" * * @param list * Array to print * @param quote * Quote string for each element of array * @param beforeString * starting string * @param afterString * ending string * @return */ private String listToString(String[] list, String quote, String beforeString, String afterString) { StringBuilder result = new StringBuilder(); if (list != null && list.length > 0) { result.append(beforeString); result.append(quote + list[0] + quote); for (int i = 1; i < list.length; i++) { result.append(", " + quote + list[i] + quote); } result.append(afterString); } return result.toString(); } /** * Converting InternalQName to String, using defined LocationFactory. * * @param name * @return * @throws RepositoryException */ private String qNameToString(InternalQName qName) throws RepositoryException { return locationFactory.createJCRName(qName).getAsString(); } }