View Javadoc
1   /*
2    * (c) Copyright 2013 Openflexo
3    *
4    * This file is part of OpenFlexo.
5    *
6    * OpenFlexo is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * OpenFlexo is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
18   *
19   */
20  
21  package org.openflexo.foundation.doc;
22  
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.List;
26  
27  import org.openflexo.foundation.doc.FlexoDocFragment.FragmentConsistencyException;
28  import org.openflexo.foundation.fml.VirtualModel;
29  import org.openflexo.foundation.resource.CannotRenameException;
30  import org.openflexo.foundation.resource.PamelaResource;
31  import org.openflexo.foundation.resource.ResourceData;
32  import org.openflexo.foundation.technologyadapter.TechnologyAdapter;
33  import org.openflexo.foundation.technologyadapter.TechnologyAdapterResource;
34  import org.openflexo.pamela.annotations.Adder;
35  import org.openflexo.pamela.annotations.CloningStrategy;
36  import org.openflexo.pamela.annotations.Embedded;
37  import org.openflexo.pamela.annotations.Finder;
38  import org.openflexo.pamela.annotations.Getter;
39  import org.openflexo.pamela.annotations.ImplementationClass;
40  import org.openflexo.pamela.annotations.ModelEntity;
41  import org.openflexo.pamela.annotations.PastingPoint;
42  import org.openflexo.pamela.annotations.PropertyIdentifier;
43  import org.openflexo.pamela.annotations.Remover;
44  import org.openflexo.pamela.annotations.Setter;
45  import org.openflexo.pamela.annotations.XMLAttribute;
46  import org.openflexo.pamela.annotations.XMLElement;
47  import org.openflexo.pamela.annotations.CloningStrategy.StrategyType;
48  import org.openflexo.pamela.annotations.Getter.Cardinality;
49  
50  /**
51   * Generic abstract concept representing a text-based document (eg .docx, .odt, etc...)<br>
52   * 
53   * Such a document is not structured, as it contains only a list of {@link FlexoDocElement}, but a structuration can be dynamically infered
54   * from the styles. See {@link #getStructuringStyles()}
55   * 
56   * @author sylvain
57   *
58   * @param <D>
59   *            type of {@link FlexoDocument} involving this concept
60   * @param <TA>
61   *            {@link TechnologyAdapter} of current implementation
62   */
63  @ModelEntity(isAbstract = true)
64  @ImplementationClass(FlexoDocument.FlexoDocumentImpl.class)
65  public interface FlexoDocument<D extends FlexoDocument<D, TA>, TA extends TechnologyAdapter<TA>>
66  		extends FlexoDocObject<D, TA>, ResourceData<D>, FlexoDocElementContainer<D, TA> {
67  
68  	public static final String ROOT_ELEMENTS_KEY = "rootElements";
69  
70  	@PropertyIdentifier(type = NamedDocStyle.class, cardinality = Cardinality.LIST)
71  	public static final String STYLES_KEY = "styles";
72  
73  	@PropertyIdentifier(type = NamedDocStyle.class, cardinality = Cardinality.LIST)
74  	public static final String STRUCTURING_STYLES_KEY = "structuringStyles";
75  
76  	@PropertyIdentifier(type = String.class)
77  	public static final String NAME_KEY = "name";
78  
79  	@Getter(value = NAME_KEY)
80  	@XMLAttribute
81  	public String getName();
82  
83  	@Setter(NAME_KEY)
84  	public void setName(String name);
85  
86  	public String getURI();
87  
88  	/**
89  	 * Return element identified by identifier, or null if no such element exists
90  	 */
91  	@Override
92  	public FlexoDocElement<D, TA> getElementWithIdentifier(String identifier);
93  
94  	/**
95  	 * Return elements matching supplied base identifier
96  	 */
97  	public List<FlexoDocElement<D, TA>> getElementsWithBaseIdentifier(String baseIdentifier);
98  
99  	/**
100 	 * Return a new list of elements of supplied type
101 	 * 
102 	 * @param elementType
103 	 * @return
104 	 */
105 	public <E> List<E> getElements(Class<E> elementType);
106 
107 	/**
108 	 * Return the list of root elements of this document, which are infered to be root while interpreting the document as a structured
109 	 * document (see {@link #getStructuringStyles()})
110 	 * 
111 	 * @return
112 	 */
113 	public List<FlexoDocElement<D, TA>> getRootElements();
114 
115 	public void invalidateRootElements();
116 
117 	public void notifyRootElementsChanged();
118 
119 	/**
120 	 * Return the list of style used in this document
121 	 * 
122 	 * @return
123 	 */
124 	@Getter(value = STYLES_KEY, cardinality = Cardinality.LIST, inverse = NamedDocStyle.DOCUMENT_KEY)
125 	@XMLElement(primary = true)
126 	@CloningStrategy(StrategyType.CLONE)
127 	@Embedded
128 	public List<NamedDocStyle<D, TA>> getStyles();
129 
130 	@Setter(STYLES_KEY)
131 	public void setStyles(List<NamedDocStyle<D, TA>> someStyles);
132 
133 	@Adder(STYLES_KEY)
134 	@PastingPoint
135 	public void addToStyles(NamedDocStyle<D, TA> aStyle);
136 
137 	@Remover(STYLES_KEY)
138 	public void removeFromStyles(NamedDocStyle<D, TA> aStyle);
139 
140 	/**
141 	 * Return an ordered list of styles to be used to present a structured document<br>
142 	 * 
143 	 * @return
144 	 */
145 	@Getter(value = STRUCTURING_STYLES_KEY, cardinality = Cardinality.LIST)
146 	public List<NamedDocStyle<D, TA>> getStructuringStyles();
147 
148 	@Setter(STRUCTURING_STYLES_KEY)
149 	public void setStructuringStyles(List<NamedDocStyle<D, TA>> someStyles);
150 
151 	@Adder(STRUCTURING_STYLES_KEY)
152 	public void addToStructuringStyles(NamedDocStyle<D, TA> aStyle);
153 
154 	@Remover(STRUCTURING_STYLES_KEY)
155 	public void removeFromStructuringStyles(NamedDocStyle<D, TA> aStyle);
156 
157 	@Finder(collection = STYLES_KEY, attribute = NamedDocStyle.NAME_KEY)
158 	public NamedDocStyle<D, TA> getStyleByName(String styleName);
159 
160 	@Finder(collection = STYLES_KEY, attribute = NamedDocStyle.STYLE_ID_KEY)
161 	public NamedDocStyle<D, TA> getStyleByIdentifier(String styleId);
162 
163 	/**
164 	 * Activate style identified by styleId
165 	 * 
166 	 * @param styleId
167 	 * @return
168 	 */
169 	public NamedDocStyle<D, TA> activateStyle(String styleId);
170 
171 	/**
172 	 * Return a collection of all identifiers of styles that can be activated for this document
173 	 * 
174 	 * @return
175 	 */
176 	public Collection<String> getKnownStyleIds();
177 
178 	/**
179 	 * Add at the end of the document a paragraph with a single run containing supplied text. The paragraph is set to supplied style.
180 	 * 
181 	 * @param style
182 	 * @param text
183 	 * @return
184 	 */
185 	public FlexoDocParagraph<D, TA> addStyledParagraphOfText(NamedDocStyle<D, TA> style, String text);
186 
187 	/**
188 	 * Insert a specified index in the document a paragraph with a single run containing supplied text. The paragraph is set to supplied
189 	 * style.
190 	 * 
191 	 * @param style
192 	 * @param text
193 	 * @param index
194 	 * @return
195 	 */
196 	public FlexoDocParagraph<D, TA> insertStyledParagraphOfTextAtIndex(NamedDocStyle<D, TA> style, String text, int index);
197 
198 	/**
199 	 * Add at the end of the document a table presets with supplied number of rows and columns, with empty paragraphs inside each cell
200 	 * 
201 	 * @param rows
202 	 *            number of rows
203 	 * @param cols
204 	 *            number of columns
205 	 * @return
206 	 */
207 	public FlexoDocTable<D, TA> addTable(int rows, int cols);
208 
209 	/**
210 	 * Insert a specified index in the document a table presets with supplied number of rows and columns, with empty paragraphs inside each
211 	 * cell style.
212 	 * 
213 	 * @param rows
214 	 *            number of rows
215 	 * @param cols
216 	 *            number of columns
217 	 * @return
218 	 */
219 	public FlexoDocTable<D, TA> insertTableAtIndex(int rows, int cols, int index);
220 
221 	/**
222 	 * Return fragment identified by start and end elements (inclusive)
223 	 * 
224 	 * @param startElement
225 	 * @param endElement
226 	 * @return
227 	 */
228 	public FlexoDocFragment<D, TA> getFragment(FlexoDocElement<D, TA> startElement, FlexoDocElement<D, TA> endElement)
229 			throws FragmentConsistencyException;
230 
231 	/**
232 	 * Return the {@link DocumentFactory} with which this document was built
233 	 * 
234 	 * @return
235 	 */
236 	public DocumentFactory<D, TA> getFactory();
237 
238 	public String debugContents();
239 
240 	public String debugStructuredContents();
241 
242 	public static abstract class FlexoDocumentImpl<D extends FlexoDocument<D, TA>, TA extends TechnologyAdapter<TA>>
243 			extends FlexoDocObjectImpl<D, TA> implements FlexoDocument<D, TA> {
244 
245 		@Override
246 		public <E> List<E> getElements(Class<E> elementType) {
247 			List<E> returned = new ArrayList<>();
248 			for (FlexoDocElement<D, TA> e : getElements()) {
249 				if (elementType.isAssignableFrom(e.getImplementedInterface())) {
250 					returned.add((E) e);
251 				}
252 			}
253 			return returned;
254 		}
255 
256 		@Override
257 		public TA getTechnologyAdapter() {
258 			if (getResource() != null) {
259 				return ((TechnologyAdapterResource<D, TA>) getResource()).getTechnologyAdapter();
260 			}
261 			return null;
262 		}
263 
264 		/**
265 		 * Return the URI of the {@link VirtualModel}<br>
266 		 * The convention for URI are following: <viewpoint_uri>/<virtual_model_name >#<flexo_concept_name>.<edition_scheme_name> <br>
267 		 * eg<br>
268 		 * http://www.mydomain.org/MyViewPoint/MyVirtualModel#MyFlexoConcept. MyEditionScheme
269 		 * 
270 		 * @return String representing unique URI of this object
271 		 */
272 		@Override
273 		public String getURI() {
274 			if (getResource() != null) {
275 				return getResource().getURI();
276 			}
277 			return null;
278 		}
279 
280 		@Override
281 		public String getName() {
282 			if (getResource() != null) {
283 				return getResource().getName();
284 			}
285 			return (String) performSuperGetter(NAME_KEY);
286 		}
287 
288 		@Override
289 		public void setName(String name) {
290 			if (requireChange(getName(), name)) {
291 				String oldValue = getName();
292 				if (getResource() != null) {
293 					try {
294 						getResource().setName(name);
295 						getPropertyChangeSupport().firePropertyChange("name", oldValue, name);
296 					} catch (CannotRenameException e) {
297 						e.printStackTrace();
298 					}
299 				}
300 				else {
301 					performSuperSetter(NAME_KEY, name);
302 				}
303 			}
304 		}
305 
306 		private List<FlexoDocElement<D, TA>> rootElements = null;
307 
308 		/**
309 		 * Return the list of root elements of this document, which are infered to be root while interpreting the document as a structured
310 		 * document (see {@link #getStructuringStyles()})
311 		 * 
312 		 * @return
313 		 */
314 		@Override
315 		public List<FlexoDocElement<D, TA>> getRootElements() {
316 			if (rootElements == null) {
317 				rootElements = computeRootElements();
318 			}
319 			return rootElements;
320 		}
321 
322 		@Override
323 		public void invalidateRootElements() {
324 			if (rootElements != null) {
325 				for (FlexoDocElement<D, TA> e : rootElements) {
326 					e.invalidateChildrenElements();
327 				}
328 			}
329 			rootElements = null;
330 		}
331 
332 		protected boolean postponeRootElementChangedNotifications = false;
333 
334 		@Override
335 		public void notifyRootElementsChanged() {
336 			if (!postponeRootElementChangedNotifications) {
337 				getPropertyChangeSupport().firePropertyChange(ROOT_ELEMENTS_KEY, null, getRootElements());
338 			}
339 		}
340 
341 		private List<FlexoDocElement<D, TA>> computeRootElements() {
342 			List<FlexoDocElement<D, TA>> returned = new ArrayList<>();
343 			Integer l = null;
344 			for (FlexoDocElement<D, TA> e : getElements()) {
345 				if (l == null) {
346 					returned.add(e);
347 					if (e instanceof FlexoDocParagraph) {
348 						if (((FlexoDocParagraph<D, TA>) e).getNamedStyle() != null
349 								&& ((FlexoDocParagraph<D, TA>) e).getNamedStyle().isLevelled()) {
350 							l = ((FlexoDocParagraph<D, TA>) e).getNamedStyle().getLevel();
351 						}
352 					}
353 				}
354 				else {
355 					if (e instanceof FlexoDocParagraph) {
356 						if (((FlexoDocParagraph<D, TA>) e).getNamedStyle() != null) {
357 							if (((FlexoDocParagraph<D, TA>) e).getNamedStyle().getLevel().equals(l)) {
358 								returned.add(e);
359 							}
360 						}
361 					}
362 				}
363 			}
364 			return returned;
365 		}
366 
367 		@Override
368 		public DocumentFactory<D, TA> getFactory() {
369 
370 			if (getResource() != null) {
371 				return (DocumentFactory<D, TA>) ((PamelaResource<?, ?>) getResource()).getFactory();
372 			}
373 			return null;
374 		}
375 
376 		@Override
377 		public FlexoDocFragment<D, TA> getFragment(FlexoDocElement<D, TA> startElement, FlexoDocElement<D, TA> endElement)
378 				throws FragmentConsistencyException {
379 			if (getFactory() != null) {
380 				return getFactory().getFragment(startElement, endElement);
381 			}
382 			return null;
383 		}
384 
385 	}
386 
387 }