View Javadoc
1   /**
2    * This file Copyright (c) 2003-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.freemarker.models;
35  
36  import static info.magnolia.jcr.util.NodeUtil.*;
37  import static java.util.stream.Collectors.toList;
38  
39  import info.magnolia.jcr.util.ContentMap;
40  
41  import java.util.ArrayList;
42  import java.util.List;
43  import java.util.stream.Stream;
44  
45  import javax.jcr.Node;
46  import javax.jcr.RepositoryException;
47  
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  
51  import freemarker.template.AdapterTemplateModel;
52  import freemarker.template.ObjectWrapper;
53  import freemarker.template.SimpleCollection;
54  import freemarker.template.TemplateCollectionModel;
55  import freemarker.template.TemplateHashModelEx;
56  import freemarker.template.TemplateModel;
57  import freemarker.template.TemplateModelException;
58  import freemarker.template.TemplateNodeModel;
59  import freemarker.template.TemplateScalarModel;
60  import freemarker.template.TemplateSequenceModel;
61  
62  /**
63   * A wrapper for Content nodes. Exposes properties using an HashModel (.property, ?size, ...) a hierarchy (TemplateNodeModel: ?children, ?parent, ...) and as a scalar (returns the node name)
64   */
65  public class ContentMapModel implements TemplateHashModelEx, TemplateNodeModel, TemplateScalarModel, AdapterTemplateModel {
66  
67      private static final Logger log = LoggerFactory.getLogger(ContentMapModel.class);
68  
69      static final MagnoliaModelFactoryelFactory.html#MagnoliaModelFactory">MagnoliaModelFactory FACTORY = new MagnoliaModelFactory() {
70          @Override
71          public Class factoryFor() {
72              return ContentMap.class;
73          }
74  
75          @Override
76          public AdapterTemplateModel create(Object object, ObjectWrapper wrapper) {
77              return new ContentMapModel((ContentMap) object, (MagnoliaObjectWrapper) wrapper);
78          }
79      };
80  
81      private final ContentMap content;
82      private final MagnoliaObjectWrapper wrapper;
83  
84      ContentMapModel(ContentMap content, MagnoliaObjectWrapper wrapper) {
85          this.content = content;
86          this.wrapper = wrapper;
87      }
88  
89      @Override
90      public String getAsString() {
91          return (String) content.get("@name");
92      }
93  
94      @Override
95      public TemplateModel get(String key) throws TemplateModelException {
96          Object value = content.get(key);
97          if (value instanceof Node) {
98              return wrapper.wrap(new ContentMap((Node)value));
99          } else {
100             return wrapper.wrap(value);
101         }
102     }
103 
104     @Override
105     public boolean isEmpty() throws TemplateModelException {
106         return (size() == 0);
107     }
108 
109     @Override
110     public int size() throws TemplateModelException {
111         return content.size();
112     }
113 
114     @Override
115     public TemplateCollectionModel keys() throws TemplateModelException {
116         return new SimpleCollection(getKeys(content).iterator(), null);
117     }
118 
119     @Override
120     public TemplateCollectionModel values() throws TemplateModelException {
121         List<Object> values = getKeys(content)
122                 .map(content::get)
123                 .collect(toList());
124         return (TemplateCollectionModel) wrapper.wrap(values.iterator());
125     }
126 
127     private static Stream<String> getKeys(ContentMap content) {
128         return content.keySet().stream()
129                 .filter(key -> !content.isSpecialProperty("@" + key));
130     }
131 
132     @Override
133     public TemplateNodeModel getParentNode() throws TemplateModelException {
134         try {
135             // todo : check if this is the root?
136             // content.getLevel() == 0;
137             final ContentMap parent = new ContentMap(content.getJCRNode().getParent());
138             return (TemplateNodeModel) wrapper.wrap(parent);
139         } catch (RepositoryException e) {
140             throw new TemplateModelException("Can't get parent of " + content + ":" + e.getMessage(), e);
141         }
142     }
143 
144     /**
145      * This returns all children, except nodes or jcr: types and mgnl:metaData.
146      */
147     @Override
148     public TemplateSequenceModel getChildNodes() throws TemplateModelException {
149         List<ContentMap> children;
150         try {
151             children = asList(getNodes(content.getJCRNode())).stream()
152                 .map(ContentMap::new)
153                 .collect(toList());
154         } catch (RepositoryException e) {
155             log.error("Failed to read children of {}", content.getJCRNode(), e);
156             children = new ArrayList<>();
157         }
158         return (TemplateSequenceModel) wrapper.wrap(children);
159     }
160 
161     @Override
162     public String getNodeName() throws TemplateModelException {
163         return (String) content.get("@name");
164     }
165 
166     @Override
167     public String getNodeType() throws TemplateModelException {
168         return content.get("@nodeType").toString();
169     }
170 
171     @Override
172     public String getNodeNamespace() throws TemplateModelException {
173         return null; // non XML implementation
174     }
175 
176     @Override
177     public Object getAdaptedObject(Class hint) {
178         return this.content;
179     }
180 
181     public Node getJCRNode() {
182         return content.getJCRNode();
183     }
184 }