001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (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.acacia.client.entity;
029
030import org.opencms.acacia.shared.CmsEntity;
031import org.opencms.acacia.shared.CmsEntityAttribute;
032import org.opencms.acacia.shared.CmsType;
033import org.opencms.gwt.client.util.CmsDomUtil;
034import org.opencms.gwt.shared.CmsGwtConstants;
035
036import java.util.ArrayList;
037import java.util.HashMap;
038import java.util.List;
039import java.util.Map;
040
041import com.google.gwt.core.client.JavaScriptObject;
042import com.google.gwt.dom.client.Document;
043import com.google.gwt.dom.client.Element;
044import com.google.gwt.dom.client.NodeList;
045
046/**
047 * The editor data back-end.<p>
048 */
049public final class CmsEntityBackend implements I_CmsEntityBackend {
050
051    /** The instance. */
052    private static CmsEntityBackend INSTANCE;
053
054    /** CmsEntity id counter. */
055    private int m_count;
056
057    /** The registered entities. */
058    private Map<String, CmsEntity> m_entities;
059
060    /** The registered types. */
061    private Map<String, CmsType> m_types;
062
063    /**
064     * Constructor.<p>
065     */
066    public CmsEntityBackend() {
067
068        m_entities = new HashMap<String, CmsEntity>();
069        m_types = new HashMap<String, CmsType>();
070    }
071
072    /**
073     * Method to create an entity object from a wrapped instance.<p>
074     *
075     * @param entityWrapper the wrappe entity
076     *
077     * @return the entity
078     */
079    public static CmsEntity createFromNativeWrapper(JavaScriptObject entityWrapper) {
080
081        CmsEntity result = new CmsEntity(null, getEntityType(entityWrapper));
082        String[] simpleAttr = getSimpleAttributeNames(entityWrapper);
083        for (int i = 0; i < simpleAttr.length; i++) {
084            String[] simpleAttrValues = getSimpleAttributeValues(entityWrapper, simpleAttr[i]);
085            for (int j = 0; j < simpleAttrValues.length; j++) {
086                result.addAttributeValue(simpleAttr[i], simpleAttrValues[j]);
087            }
088        }
089        String[] complexAttr = getComplexAttributeNames(entityWrapper);
090        for (int i = 0; i < complexAttr.length; i++) {
091            JavaScriptObject[] complexAttrValues = getComplexAttributeValues(entityWrapper, complexAttr[i]);
092            for (int j = 0; j < complexAttrValues.length; j++) {
093                result.addAttributeValue(complexAttr[i], createFromNativeWrapper(complexAttrValues[j]));
094            }
095        }
096        return result;
097    }
098
099    /**
100     * Returns the instance.<p>
101     *
102     * @return the instance
103     */
104    public static CmsEntityBackend getInstance() {
105
106        if (INSTANCE == null) {
107            INSTANCE = new CmsEntityBackend();
108        }
109        return INSTANCE;
110    }
111
112    /**
113     * Returns the complex attribute names of the given entity.<p>
114     *
115     * @param entityWrapper the wrapped entity
116     *
117     * @return the complex attribute names
118     */
119    private static native String[] getComplexAttributeNames(JavaScriptObject entityWrapper)/*-{
120                                                                                           var attr = entityWrapper.getAttributes();
121                                                                                           var result = [];
122                                                                                           for (i = 0; i < attr.length; i++) {
123                                                                                           if (!attr[i].isSimpleValue()) {
124                                                                                           result.push(attr[i].getAttributeName());
125                                                                                           }
126                                                                                           }
127                                                                                           return result;
128                                                                                           }-*/;
129
130    /**
131     * Returns the complex attribute values of the given entity.<p>
132     *
133     * @param entityWrapper the wrapped entity
134     * @param attributeName the attribute name
135     *
136     * @return the complex attribute values
137     */
138    private static native JavaScriptObject[] getComplexAttributeValues(
139        JavaScriptObject entityWrapper,
140        String attributeName)/*-{
141                             return entityWrapper.getAttribute(attributeName).getComplexValues();
142                             }-*/;
143
144    /**
145     * Returns the entity type.<p>
146     *
147     * @param entityWrapper the wrapped entity
148     *
149     * @return the entity type name
150     */
151    private static native String getEntityType(JavaScriptObject entityWrapper)/*-{
152                                                                              return entityWrapper.getTypeName();
153                                                                              }-*/;
154
155    /**
156     * Returns the simple attribute names of the given entity.<p>
157     *
158     * @param entityWrapper the wrapped entity
159     *
160     * @return the simple attribute names
161     */
162    private static native String[] getSimpleAttributeNames(JavaScriptObject entityWrapper)/*-{
163                                                                                          var attr = entityWrapper.getAttributes();
164                                                                                          var result = [];
165                                                                                          for (i = 0; i < attr.length; i++) {
166                                                                                          if (attr[i].isSimpleValue()) {
167                                                                                          result.push(attr[i].getAttributeName());
168                                                                                          }
169                                                                                          }
170                                                                                          return result;
171                                                                                          }-*/;
172
173    /**
174     * Returns the simple attribute values of the given entity.<p>
175     *
176     * @param entityWrapper the wrapped entity
177     * @param attributeName the attribute name
178     *
179     * @return the simple attribute values
180     */
181    private static native String[] getSimpleAttributeValues(JavaScriptObject entityWrapper, String attributeName)/*-{
182                                                                                                                 return entityWrapper.getAttribute(attributeName).getSimpleValues();
183                                                                                                                 }-*/;
184
185    /**
186     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#changeEntityContentValues(org.opencms.acacia.shared.CmsEntity, org.opencms.acacia.shared.CmsEntity)
187     */
188    public void changeEntityContentValues(CmsEntity original, CmsEntity newContent) {
189
190        clearEntityAttributes(original);
191        for (CmsEntityAttribute attribute : newContent.getAttributes()) {
192            if (attribute.isSimpleValue()) {
193                for (String value : attribute.getSimpleValues()) {
194                    original.addAttributeValue(attribute.getAttributeName(), value);
195                }
196            } else {
197                for (CmsEntity value : attribute.getComplexValues()) {
198                    original.addAttributeValue(attribute.getAttributeName(), registerEntity(value));
199                }
200            }
201        }
202    }
203
204    /**
205     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#clearEntities()
206     */
207    public void clearEntities() {
208
209        m_entities.clear();
210    }
211
212    /**
213     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#createEntity(java.lang.String, java.lang.String)
214     */
215    public CmsEntity createEntity(String entityId, String entityType) {
216
217        if (entityId == null) {
218            entityId = generateId();
219        }
220        if (!m_types.containsKey(entityType)) {
221            throw new IllegalArgumentException("Type " + entityType + " is not registered yet");
222        }
223        if (m_entities.containsKey(entityId)) {
224            throw new IllegalArgumentException("CmsEntity " + entityId + " is already registered");
225        }
226        CmsEntity entity = new CmsEntity(entityId, entityType);
227        m_entities.put(entityId, entity);
228        return entity;
229    }
230
231    /**
232     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#createType(java.lang.String)
233     */
234    public CmsType createType(String id) {
235
236        if (m_types.containsKey(id)) {
237            throw new IllegalArgumentException("Type " + id + " is already registered");
238        }
239        CmsType type = new CmsType(id);
240        m_types.put(id, type);
241        return type;
242    }
243
244    /**
245     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#getAttributeElements(org.opencms.acacia.shared.CmsEntity, java.lang.String, com.google.gwt.dom.client.Element)
246     */
247    public List<Element> getAttributeElements(CmsEntity entity, String attributeName, Element context) {
248
249        return getAttributeElements(entity.getId(), attributeName, context);
250    }
251
252    /**
253     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#getAttributeElements(java.lang.String, java.lang.String, com.google.gwt.dom.client.Element)
254     */
255    public List<Element> getAttributeElements(String entityId, String attributeName, Element context) {
256
257        StringBuffer select = new StringBuffer();
258        select.append("[").append(CmsGwtConstants.ATTR_DATA_ID).append("='").append(entityId).append("'][").append(
259            CmsGwtConstants.ATTR_DATA_FIELD).append("$='").append(attributeName).append("'], ");
260        select.append("[").append(CmsGwtConstants.ATTR_DATA_ID).append("='").append(entityId).append("'] [").append(
261            CmsGwtConstants.ATTR_DATA_FIELD).append("$='").append(attributeName).append("'], ");
262        select.append("[").append(CmsGwtConstants.ATTR_DATA_ID).append("='").append(entityId).append("'][").append(
263            CmsGwtConstants.ATTR_DATA_FIELD).append("*='").append(attributeName).append("['], ");
264        select.append("[").append(CmsGwtConstants.ATTR_DATA_ID).append("='").append(entityId).append("'] [").append(
265            CmsGwtConstants.ATTR_DATA_FIELD).append("*='").append(attributeName).append("[']");
266
267        if (context == null) {
268            context = Document.get().getDocumentElement();
269        }
270        return select(select.toString(), context);
271    }
272
273    /**
274     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#getEntity(java.lang.String)
275     */
276    public CmsEntity getEntity(String entityId) {
277
278        return m_entities.get(entityId);
279    }
280
281    /**
282     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#getType(java.lang.String)
283     */
284    public CmsType getType(String id) {
285
286        return m_types.get(id);
287    }
288
289    /**
290     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#registerEntity(org.opencms.acacia.shared.CmsEntity)
291     */
292    public CmsEntity registerEntity(CmsEntity entity) {
293
294        if (m_entities.containsKey(entity.getId())) {
295            throw new IllegalArgumentException("CmsEntity " + entity.getId() + " is already registered");
296        }
297        if (!m_types.containsKey(entity.getTypeName())) {
298            throw new IllegalArgumentException("Type " + entity.getTypeName() + " is not registered yet");
299        }
300        for (CmsEntityAttribute attr : entity.getAttributes()) {
301            if (attr.isComplexValue()) {
302                for (CmsEntity child : attr.getComplexValues()) {
303                    try {
304                        registerEntity(child);
305                    } catch (Exception e) {
306                        // in case registering the entity attribute fails, remove it
307                        entity.removeAttributeSilent(attr.getAttributeName());
308                    }
309                }
310            }
311        }
312        entity.ensureChangeHandlers();
313        m_entities.put(entity.getId(), entity);
314        return entity;
315    }
316
317    /**
318     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#registerEntity(org.opencms.acacia.shared.CmsEntity, boolean)
319     */
320    public CmsEntity registerEntity(CmsEntity entity, boolean discardIds) {
321
322        if (!discardIds) {
323            return registerEntity(entity);
324        } else {
325            if (!m_types.containsKey(entity.getTypeName())) {
326                throw new IllegalArgumentException("Type " + entity.getTypeName() + " is not registered yet");
327            }
328            CmsEntity result = createEntity(null, entity.getTypeName());
329            for (CmsEntityAttribute attr : entity.getAttributes()) {
330                if (attr.isComplexValue()) {
331                    for (CmsEntity child : attr.getComplexValues()) {
332                        try {
333                            result.addAttributeValue(attr.getAttributeName(), registerEntity(child, discardIds));
334                        } catch (Exception e) {
335                            // in case registering the entity attribute fails, do not add the attribute value
336                        }
337                    }
338                } else {
339                    for (String value : attr.getSimpleValues()) {
340                        result.addAttributeValue(attr.getAttributeName(), value);
341                    }
342                }
343
344            }
345            result.ensureChangeHandlers();
346            m_entities.put(result.getId(), result);
347            return result;
348        }
349    }
350
351    /**
352     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#registerTypes(org.opencms.acacia.shared.CmsType, java.util.Map)
353     */
354    public void registerTypes(CmsType type, Map<String, CmsType> types) {
355
356        if (!m_types.containsKey(type.getId())) {
357            for (String attrName : type.getAttributeNames()) {
358                String subTypeId = type.getAttributeTypeName(attrName);
359                CmsType subType = types.get(subTypeId);
360                if (subType == null) {
361                    throw new IllegalArgumentException("Type information for " + subTypeId + " is missing");
362                }
363                registerTypes(subType, types);
364            }
365            m_types.put(type.getId(), type);
366        }
367    }
368
369    /**
370     * @see org.opencms.acacia.client.entity.I_CmsEntityBackend#removeEntity(java.lang.String)
371     */
372    public void removeEntity(String entityId) {
373
374        if (m_entities.containsKey(entityId)) {
375            CmsEntity entity = m_entities.get(entityId);
376            for (CmsEntityAttribute attr : entity.getAttributes()) {
377                if (attr.isComplexValue()) {
378                    for (CmsEntity child : attr.getComplexValues()) {
379                        removeEntity(child.getId());
380                    }
381                }
382            }
383            m_entities.remove(entityId);
384        }
385    }
386
387    /**
388     * Returns a list of DOM elements matching the given selector.<p>
389     *
390     * @param selector the CSS selector
391     * @param context the context element
392     *
393     * @return the matching elements
394     */
395    protected List<Element> select(String selector, Element context) {
396
397        NodeList<Element> elements = CmsDomUtil.querySelectorAll(selector, context);
398        List<Element> result = new ArrayList<Element>();
399        for (int i = 0; i < elements.getLength(); i++) {
400            result.add(elements.getItem(i));
401        }
402        return result;
403    }
404
405    /**
406     * Removes all attributes from the given entity.<p>
407     *
408     * @param entity the entity
409     */
410    private void clearEntityAttributes(CmsEntity entity) {
411
412        for (CmsEntityAttribute attribute : entity.getAttributes()) {
413            if (attribute.isComplexValue()) {
414                for (CmsEntity child : attribute.getComplexValues()) {
415                    clearEntityAttributes(child);
416                    removeEntity(child.getId());
417                }
418            }
419            entity.removeAttributeSilent(attribute.getAttributeName());
420        }
421    }
422
423    /**
424     * Generates a new entity id.<p>
425     *
426     * @return the generated id
427     */
428    private String generateId() {
429
430        m_count++;
431        String id = "generic_id" + m_count;
432        while (m_entities.containsKey(id)) {
433            m_count++;
434            id = "generic_id" + m_count;
435        }
436        return id;
437    }
438}