001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.xml.xml2json.document; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsResource; 032import org.opencms.file.CmsResourceFilter; 033import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 034import org.opencms.file.types.CmsResourceTypeXmlContent; 035import org.opencms.i18n.CmsLocaleManager; 036import org.opencms.json.JSONArray; 037import org.opencms.json.JSONException; 038import org.opencms.json.JSONObject; 039import org.opencms.main.CmsException; 040import org.opencms.main.OpenCms; 041import org.opencms.relations.CmsRelation; 042import org.opencms.relations.CmsRelationFilter; 043import org.opencms.util.CmsStringUtil; 044import org.opencms.xml.content.CmsXmlContent; 045import org.opencms.xml.content.CmsXmlContentFactory; 046import org.opencms.xml.content.I_CmsXmlContentHandler; 047import org.opencms.xml.content.I_CmsXmlContentHandler.JsonRendererSettings; 048import org.opencms.xml.xml2json.CmsJsonRequest; 049import org.opencms.xml.xml2json.handler.CmsJsonHandlerException; 050import org.opencms.xml.xml2json.handler.CmsJsonHandlerXmlContent.PathNotFoundException; 051import org.opencms.xml.xml2json.renderer.CmsJsonRendererXmlContent; 052import org.opencms.xml.xml2json.renderer.I_CmsJsonRendererXmlContent; 053 054import java.util.Collections; 055import java.util.List; 056import java.util.Locale; 057import java.util.Map; 058 059import org.apache.commons.lang3.StringUtils; 060 061/** 062 * Class representing a JSON document for an XML content. 063 */ 064public class CmsJsonDocumentXmlContent extends CmsJsonDocumentResource { 065 066 /** The XML content. */ 067 protected CmsXmlContent m_xmlContent; 068 069 /** Whether to throw exceptions. */ 070 protected boolean m_throwException = true; 071 072 /** Whether to embed linked contents. */ 073 protected boolean m_embedLinkedModelgroup = true; 074 075 /** The JSON part in the case of a path request. */ 076 private Object m_jsonPart; 077 078 /** The XML content renderer. */ 079 private I_CmsJsonRendererXmlContent m_renderer; 080 081 /** 082 * Creates a new JSON document.<p> 083 * 084 * @param jsonRequest the JSON request 085 * @param xmlContent the XML content 086 * @throws Exception if something goes wrong 087 */ 088 public CmsJsonDocumentXmlContent(CmsJsonRequest jsonRequest, CmsXmlContent xmlContent) 089 throws Exception { 090 091 this(jsonRequest, xmlContent, true); 092 } 093 094 /** 095 * Creates a new JSON document.<p> 096 * 097 * @param jsonRequest the JSON request 098 * @param xmlContent the XML content 099 * @param embedLinkedModelgroup whether to embed linked model groups 100 * @throws Exception if something goes wrong 101 */ 102 public CmsJsonDocumentXmlContent( 103 CmsJsonRequest jsonRequest, 104 CmsXmlContent xmlContent, 105 boolean embedLinkedModelgroup) 106 throws Exception { 107 108 super(jsonRequest, xmlContent.getFile()); 109 m_xmlContent = xmlContent; 110 m_embedLinkedModelgroup = embedLinkedModelgroup; 111 initRenderer(); 112 } 113 114 /** 115 * @see org.opencms.xml.xml2json.document.CmsJsonDocumentResource#getJson() 116 */ 117 @Override 118 public Object getJson() 119 throws JSONException, CmsException, CmsJsonHandlerException, PathNotFoundException, Exception { 120 121 insertJsonContent(); 122 insertJsonWrapper(); 123 if (isShowLinkedContentsRequest()) { 124 insertJsonLinkedContents(); 125 } 126 return m_jsonPart != null ? m_jsonPart : m_json; 127 } 128 129 /** 130 * Inserts a JSON representation of a linked content into this JSON document.<p> 131 * 132 * @param resource the resource 133 * @throws Exception if something goes wrong 134 */ 135 protected void insertJsonLinkedContent(CmsResource resource) throws Exception { 136 137 JSONObject jsonObject = (JSONObject)m_json.get(FIELD_LINKED_CONTENTS); 138 String key = resource.getRootPath(); 139 try { 140 CmsFile file = m_context.getCms().readFile(resource); 141 CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(m_context.getCms(), file); 142 Object value = null; 143 if (CmsResourceTypeXmlContainerPage.isContainerPage(resource) && m_embedLinkedModelgroup) { 144 CmsJsonDocumentContainerPage document = new CmsJsonDocumentContainerPage( 145 m_jsonRequest, 146 xmlContent, 147 false); 148 value = document.getJson(); 149 } else if (CmsResourceTypeXmlContent.isXmlContent(resource)) { 150 CmsJsonDocumentXmlContent document = new CmsJsonDocumentXmlContent(m_jsonRequest, xmlContent, false); 151 value = document.getJson(); 152 } 153 jsonObject.put(key, value); 154 } catch (Exception e) { 155 JSONObject exception = new JSONObject(); 156 exception.put("exception", e.getLocalizedMessage()); 157 jsonObject.put(key, exception); 158 } 159 } 160 161 /** 162 * For each linked content, inserts a JSON representation of the linked content into this JSON document.<p> 163 * 164 * @throws Exception if something goes wrong 165 */ 166 protected void insertJsonLinkedContents() throws Exception { 167 168 List<CmsRelation> relationList = m_context.getCms().getRelationsForResource( 169 m_xmlContent.getFile(), 170 CmsRelationFilter.TARGETS); 171 m_json.put(FIELD_LINKED_CONTENTS, new JSONObject()); 172 for (CmsRelation relation : relationList) { 173 CmsResource resource = relation.getTarget(m_context.getCms(), CmsResourceFilter.DEFAULT); 174 if (CmsResourceTypeXmlContent.isXmlContent(resource) 175 || CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 176 insertJsonLinkedContent(resource); 177 } 178 } 179 } 180 181 /** 182 * Inserts a wrapper with resource information into this JSON document.<p> 183 * 184 * @throws JSONException if JSON rendering fails 185 * @throws CmsException if reading resource properties fails 186 */ 187 protected void insertJsonWrapper() throws JSONException, CmsException { 188 189 if (isShowWrapperRequest()) { 190 insertJsonLocales(); 191 insertJsonResource(); 192 } 193 } 194 195 /** 196 * Whether all locales of this XML content are requested.<p> 197 * 198 * @return whether all or not 199 */ 200 protected boolean isLocaleAllRequest() { 201 202 String paramLocale = m_jsonRequest.getParamLocale(); 203 String paramPath = m_jsonRequest.getParamPath(); 204 return (paramLocale == null) && (paramPath == null); 205 } 206 207 /** 208 * Whether one locale of this XML content is requested.<p> 209 * 210 * @return whether one locale or not 211 */ 212 protected boolean isLocalePathRequest() { 213 214 String paramLocale = m_jsonRequest.getParamLocale(); 215 String paramPath = m_jsonRequest.getParamPath(); 216 return (paramLocale != null) && (paramPath != null); 217 218 } 219 220 /** 221 * Whether a part of a locale of this XML content is requested.<p> 222 * 223 * @return whether a part of a locale or not 224 */ 225 protected boolean isLocaleRequest() { 226 227 String paramLocale = m_jsonRequest.getParamLocale(); 228 String paramPath = m_jsonRequest.getParamPath(); 229 return (paramLocale != null) && (paramPath == null); 230 } 231 232 /** 233 * Whether the default locale content shall be shown in the case 234 * the requested locale is not available.<p> 235 * 236 * @return whether to show the default locale or not 237 */ 238 protected boolean isShowFallbackLocaleRequest() { 239 240 return m_jsonRequest.getParamFallbackLocale().booleanValue(); 241 } 242 243 /** 244 * Whether all linked contents shall be embedded into this document. 245 * 246 * @return whether to embed the linked contents or not 247 */ 248 protected boolean isShowLinkedContentsRequest() { 249 250 return m_jsonRequest.getParamContent().booleanValue(); 251 } 252 253 /** 254 * Whether to show the wrapper with resource information. For backward 255 * compatibility the wrapper is shown for the all locale request but not for 256 * the locale and locale path request as a default. This default behavior 257 * can be changed by means of the "wrapper" request parameter. For locale 258 * path requests, wrapper information is not available. 259 * 260 * @return whether to show the wrapper or not 261 */ 262 protected boolean isShowWrapperRequest() { 263 264 boolean showWrapper = true; 265 String paramWrapperRaw = m_context.getParameters().get(CmsJsonRequest.PARAM_WRAPPER); 266 Boolean paramWrapper = m_jsonRequest.getParamWrapper(); 267 if (isLocaleAllRequest()) { 268 showWrapper = true; 269 if ((paramWrapperRaw != null) && (paramWrapperRaw.equals("false"))) { 270 showWrapper = false; 271 } 272 } else if (isLocaleRequest()) { 273 showWrapper = false; 274 if (paramWrapper.booleanValue()) { 275 showWrapper = true; 276 } 277 } else if (isLocalePathRequest()) { 278 showWrapper = false; 279 } 280 return showWrapper; 281 } 282 283 /** 284 * Initializes the content renderer.<p> 285 * 286 * @throws Exception if something goes wrong 287 */ 288 private void initRenderer() throws Exception { 289 290 I_CmsXmlContentHandler handler = m_xmlContent.getContentDefinition().getContentHandler(); 291 JsonRendererSettings settings = handler.getJsonRendererSettings(); 292 if (settings == null) { 293 m_renderer = new CmsJsonRendererXmlContent(); 294 } else { 295 m_renderer = (I_CmsJsonRendererXmlContent)Class.forName(settings.getClassName()).newInstance(); 296 for (Map.Entry<String, String> entry : settings.getParameters().entrySet()) { 297 m_renderer.addConfigurationParameter(entry.getKey(), entry.getValue()); 298 } 299 m_renderer.initConfiguration(); 300 } 301 m_renderer.initialize(m_context.getCms()); 302 } 303 304 /** 305 * Inserts a JSON representation of this XML content into this JSON document.<p> 306 * 307 * @throws CmsJsonHandlerException if something goes wrong 308 * @throws JSONException if JSON rendering fails 309 * @throws PathNotFoundException if path selection fails 310 */ 311 private void insertJsonContent() throws CmsJsonHandlerException, JSONException, PathNotFoundException { 312 313 if (isLocaleAllRequest()) { 314 insertJsonContentAllLocales(); 315 } else if (isLocaleRequest()) { 316 insertJsonContentLocale(); 317 } else if (isLocalePathRequest()) { 318 insertJsonContentLocalePath(); 319 } else { 320 throw new CmsJsonHandlerException("Can not use path parameter without locale parameter."); 321 } 322 } 323 324 /** 325 * Inserts all locale contents into this JSON document.<p> 326 * 327 * @throws JSONException if JSON rendering fails 328 */ 329 private void insertJsonContentAllLocales() throws JSONException { 330 331 m_json = CmsJsonRendererXmlContent.renderAllLocales(m_xmlContent, m_renderer); 332 } 333 334 /** 335 * Inserts one locale content into this JSON document.<p> 336 * 337 * @throws JSONException if JSON rendering fails 338 * @throws PathNotFoundException if the selected locale does not exist and 339 * no fallbackLocale parameter is given 340 */ 341 private void insertJsonContentLocale() throws JSONException, PathNotFoundException { 342 343 String paramLocale = m_jsonRequest.getParamLocale(); 344 Locale locale = CmsLocaleManager.getLocale(paramLocale); 345 Locale selectedLocale = OpenCms.getLocaleManager().getBestMatchingLocale( 346 locale, 347 Collections.emptyList(), 348 m_xmlContent.getLocales()); 349 boolean localeExists = true; 350 if ((selectedLocale == null) || !m_xmlContent.hasLocale(selectedLocale)) { 351 localeExists = false; 352 } 353 JSONObject jsonObject = new JSONObject(true); 354 if (localeExists) { 355 jsonObject = (JSONObject)m_renderer.render(m_xmlContent, selectedLocale); 356 } else if (isShowFallbackLocaleRequest()) { 357 List<Locale> localeList = m_xmlContent.getLocales(); 358 if (!localeList.isEmpty()) { 359 jsonObject = (JSONObject)m_renderer.render(m_xmlContent, localeList.get(0)); 360 m_json.put("localeFallback", localeList.get(0).toString()); 361 } 362 } else if (m_throwException) { 363 throw new PathNotFoundException("Locale <" + this.m_jsonRequest.getParamLocale() + "> not found."); 364 } 365 if (isShowWrapperRequest()) { 366 m_json.put("localeContent", jsonObject); 367 } else { 368 m_json = jsonObject; 369 } 370 } 371 372 /** 373 * Inserts a fragment of one locale content into this JSON document.<p> 374 * 375 * @throws PathNotFoundException if the path selection fails 376 * @throws JSONException if JSON rendering fails 377 */ 378 private void insertJsonContentLocalePath() throws PathNotFoundException, JSONException { 379 380 insertJsonContentLocale(); 381 String paramPath = m_jsonRequest.getParamPath(); 382 String[] tokens = paramPath.split("[/\\[\\]]"); 383 Object current = m_json; 384 for (String token : tokens) { 385 if (CmsStringUtil.isEmptyOrWhitespaceOnly(token)) { 386 continue; 387 } 388 if (StringUtils.isNumeric(token) && (current instanceof JSONArray)) { 389 current = ((JSONArray)current).get(Integer.parseInt(token)); 390 } else if (current instanceof JSONObject) { 391 current = ((JSONObject)current).get(token); 392 } else { 393 throw new PathNotFoundException("Path not found"); 394 } 395 } 396 m_jsonPart = current; 397 } 398 399 /** 400 * Insert a list of all locale names available for this content.<p> 401 * 402 * @throws JSONException if JSON rendering fails 403 */ 404 private void insertJsonLocales() throws JSONException { 405 406 JSONArray locales = new JSONArray(); 407 for (Locale locale : m_xmlContent.getLocales()) { 408 locales.put(locale.toString()); 409 } 410 m_json.put("locales", locales); 411 } 412}