View Javadoc
1   /*
2    * Copyright 2012-2024 Bloomreach
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.onehippo.forge.utilities.commons.jcrmockup;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import javax.jcr.Item;
23  import javax.jcr.Property;
24  import javax.jcr.PropertyType;
25  import javax.jcr.RepositoryException;
26  import javax.jcr.Value;
27  import javax.jcr.nodetype.PropertyDefinition;
28  import jakarta.xml.bind.annotation.XmlAttribute;
29  import jakarta.xml.bind.annotation.XmlElement;
30  import jakarta.xml.bind.annotation.XmlTransient;
31  
32  import org.mockito.Matchers;
33  import org.mockito.Mockito;
34  import org.mockito.invocation.InvocationOnMock;
35  import org.mockito.stubbing.Answer;
36  
37  /**
38   * JAXB annotated backed javax.jcr.Property
39   */
40  public class MockProperty {
41  
42      @XmlTransient
43      private boolean removed;
44  
45      @XmlTransient
46      private MockNode parent;   // NOSONAR (ignore field not initialized in constructor, null is valid value)
47  
48      @XmlAttribute(name = "name", namespace = "http://www.jcp.org/jcr/sv/1.0")
49      private String name;       // NOSONAR (ignore field not initialized in constructor, set by XStream)
50  
51      @XmlAttribute(name = "type", namespace = "http://www.jcp.org/jcr/sv/1.0")
52      private String type;       // NOSONAR (ignore field not initialized in constructor, set by XStream)
53  
54      @XmlElement(name = "value", namespace = "http://www.jcp.org/jcr/sv/1.0")
55      private List<String> values;  // NOSONAR (ignore field not initialized in constructor, null is valid value)
56  
57      public String getMockPropertyName() {
58          return this.name;
59      }
60  
61      public void setMockPropertyName(String name) {
62          this.name = name;
63      }
64  
65      public String getMockPropertyType() {
66          return type;
67      }
68  
69      public void setMockPropertyType(String type) {
70          this.type = type;
71      }
72  
73      public List<String> getMockValues() {
74          return values;
75      }
76  
77      public void setMockValues(List<String> values) {
78          this.values = values;
79      }
80  
81      public MockNode getParent() {
82          return this.parent;
83      }
84  
85      public void setParent(MockNode parentNode) {
86          this.parent = parentNode;
87      }
88  
89      public String getPath() {
90          if (this.parent == null) {
91              return this.name;
92          } else {
93              StringBuilder b = new StringBuilder(this.parent.getPath());
94              b.append('/');
95              b.append(this.name);
96              return b.toString();
97          }
98      }
99  
100     public void setRemoved(boolean removed) {
101         this.removed = removed;
102     }
103 
104     public boolean isRemoved() {
105         return this.removed;
106     }
107 
108     /**
109      * Gets the mocked jcr property
110      *
111      * @return the mocked jcr property backed by this object.
112      * @throws RepositoryException if mocking the property fails
113      */
114     public Property mockJcrProperty() throws RepositoryException {
115         final MockProperty mockProperty = this;
116         final Property jcrProperty = Mockito.mock(Property.class);
117         Mockito.when(jcrProperty.getName()).thenReturn(this.name);
118         Mockito.when(jcrProperty.getPath()).thenAnswer(new PathAnswer(mockProperty));
119         Mockito.when(jcrProperty.getParent()).thenAnswer(new ParentAnswer(mockProperty));
120 
121         mockType(jcrProperty);
122         if (this.values != null) {
123             if (this.values.size() == 1) {
124                 mockSingleValueProperty(jcrProperty);
125             } else if (this.values.size() > 1) {
126                 mockMultiValueProperty(jcrProperty);
127             }
128         }
129 
130         final PropertyDefinition definition = Mockito.mock(PropertyDefinition.class);
131         Mockito.when(jcrProperty.getDefinition()).thenReturn(definition);
132 
133         // if values is null, there was a property in the xml which is of type multiple but has no values
134         Mockito.when(definition.isMultiple()).thenReturn(this.values == null || this.values.size() > 1);
135 
136         final UnsupportedOperationException unsupportedOperation = new UnsupportedOperationException("The method set value is not supported yet.");
137         Mockito.doThrow(unsupportedOperation).when(jcrProperty).setValue(Matchers.anyString());
138         Mockito.doThrow(unsupportedOperation).when(jcrProperty).setValue(Matchers.anyDouble());
139         Mockito.doThrow(unsupportedOperation).when(jcrProperty).setValue(Matchers.anyBoolean());
140         Mockito.doThrow(unsupportedOperation).when(jcrProperty).setValue(Matchers.anyLong());
141         Mockito.doThrow(unsupportedOperation).when(jcrProperty).setValue(Matchers.any(Value.class));
142         Mockito.doThrow(unsupportedOperation).when(jcrProperty).setValue(Matchers.any(Value[].class));
143         Mockito.doThrow(unsupportedOperation).when(jcrProperty).setValue(Matchers.any(String[].class));
144 
145         return jcrProperty;
146     }
147 
148     private void mockMultiValueProperty(final Property jcrProperty) throws RepositoryException {
149         final List<Value> jcrValues = new ArrayList<Value>();
150         for (String value : this.values) {
151             jcrValues.add(mockValue(value));
152         }
153         final Value[] valuesArray = new Value[jcrValues.size()];
154         Mockito.when(jcrProperty.getValues()).thenReturn(jcrValues.toArray(valuesArray));
155     }
156 
157     private void mockSingleValueProperty(final Property jcrProperty) throws RepositoryException {
158         final String stringValue = this.values.get(0);
159         Mockito.when(jcrProperty.getString()).thenReturn(stringValue);
160 
161         if ("Date".equals(this.type)) {
162             Mockito.when(jcrProperty.getDate()).thenReturn(ISO8601.parse(stringValue));
163         } else if ("Boolean".equals(this.type)) {
164             Mockito.when(jcrProperty.getBoolean()).thenReturn(Boolean.parseBoolean(stringValue));
165         } else if ("Double".equals(this.type)) {
166             Mockito.when(jcrProperty.getDouble()).thenReturn(Double.parseDouble(stringValue));
167         } else if ("Long".equals(this.type)) {
168             Mockito.when(jcrProperty.getLong()).thenReturn(Long.parseLong(stringValue));
169         }
170 
171         final Value mockValue = mockValue(stringValue);
172         Mockito.when(jcrProperty.getValue()).thenReturn(mockValue);
173     }
174 
175     private void mockType(final Property jcrProperty) throws RepositoryException {
176         if ("Date".equals(this.type)) {
177             Mockito.when(jcrProperty.getType()).thenReturn(PropertyType.DATE);
178         } else if ("Boolean".equals(this.type)) {
179             Mockito.when(jcrProperty.getType()).thenReturn(PropertyType.BOOLEAN);
180         } else if ("Double".equals(this.type)) {
181             Mockito.when(jcrProperty.getType()).thenReturn(PropertyType.DOUBLE);
182         } else if ("Long".equals(this.type)) {
183             Mockito.when(jcrProperty.getType()).thenReturn(PropertyType.LONG);
184         } else if ("String".equals(this.type)) {
185             Mockito.when(jcrProperty.getType()).thenReturn(PropertyType.STRING);
186         }
187     }
188 
189     private Value mockValue(String stringValue) throws RepositoryException {
190         Value value = Mockito.mock(Value.class);
191         Mockito.when(value.getString()).thenReturn(stringValue);
192         if ("Date".equals(this.type)) {
193             Mockito.when(value.getDate()).thenReturn(ISO8601.parse(stringValue));
194             Mockito.when(value.getType()).thenReturn(PropertyType.DATE);
195         } else if ("Boolean".equals(this.type)) {
196             Mockito.when(value.getBoolean()).thenReturn(Boolean.parseBoolean(stringValue));
197             Mockito.when(value.getType()).thenReturn(PropertyType.BOOLEAN);
198         } else if ("Double".equals(this.type)) {
199             Mockito.when(value.getDouble()).thenReturn(Double.parseDouble(stringValue));
200             Mockito.when(value.getType()).thenReturn(PropertyType.DOUBLE);
201         } else if ("Long".equals(this.type)) {
202             Mockito.when(value.getLong()).thenReturn(Long.parseLong(stringValue));
203             Mockito.when(value.getType()).thenReturn(PropertyType.LONG);
204         } else if ("String".equals(this.type) || "String[]".equals(this.type)) {
205             Mockito.when(value.getType()).thenReturn(PropertyType.STRING);
206         }
207 
208         return value;
209     }
210 
211     private static class PathAnswer implements Answer<String> {
212         private final MockProperty mockProperty;
213 
214         public PathAnswer(final MockProperty mockProperty) {
215             this.mockProperty = mockProperty;
216         }
217 
218         public String answer(final InvocationOnMock invocationOnMock) {
219             return mockProperty.getPath();
220         }
221     }
222 
223     private static class ParentAnswer implements Answer<Item> {
224         private final MockProperty mockProperty;
225 
226         public ParentAnswer(final MockProperty mockProperty) {
227             this.mockProperty = mockProperty;
228         }
229 
230         public Item answer(final InvocationOnMock invocationOnMock) throws RepositoryException {
231             return mockProperty.getParent().getJcrMock();
232         }
233     }
234 }