/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.xpn.xwiki.objects; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.dom4j.Element; import org.xwiki.model.EntityType; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.model.reference.EntityReference; import org.xwiki.model.reference.SpaceReference; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.doc.merge.MergeConfiguration; import com.xpn.xwiki.doc.merge.MergeResult; import com.xpn.xwiki.objects.classes.BaseClass; import com.xpn.xwiki.objects.classes.PropertyClass; import com.xpn.xwiki.web.Utils; public class BaseObject extends BaseCollection<BaseObjectReference> implements ObjectInterface, Serializable, Cloneable { private String guid; /** * Used to resolve a string into a proper Document Reference using the current document's reference to fill the * blanks, except for the page name for which the default page name is used instead and for the wiki name for which * the current wiki is used instead of the current document reference's wiki. */ private DocumentReferenceResolver<String> currentMixedDocumentReferenceResolver2; private DocumentReferenceResolver<String> getCurrentMixedDocumentReferenceResolver() { if (this.currentMixedDocumentReferenceResolver2 == null) { this.currentMixedDocumentReferenceResolver2 = Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, "currentmixed"); } return this.currentMixedDocumentReferenceResolver2; } /** * {@inheritDoc} * <p> * Note: This method is overridden to add the deprecation warning so that code using it can see it's deprecated. * </p> * * @deprecated since 2.2M2 use {@link #getDocumentReference()} */ @Deprecated @Override public String getName() { return super.getName(); } /** * {@inheritDoc} * <p> * Note: BaseElement.setName() does not support setting reference anymore since 2.4M2. * </p> * * @deprecated since 2.2M2 use {@link #setDocumentReference(org.xwiki.model.reference.DocumentReference)} */ @Deprecated @Override public void setName(String name) { DocumentReference reference = getDocumentReference(); if (reference != null) { EntityReference relativeReference = getRelativeEntityReferenceResolver().resolve(name, EntityType.DOCUMENT); reference = new DocumentReference(relativeReference.extractReference(EntityType.DOCUMENT).getName(), new SpaceReference(relativeReference.extractReference(EntityType.SPACE).getName(), reference.getParent().getParent())); } else { reference = getCurrentMixedDocumentReferenceResolver().resolve(name); } setDocumentReference(reference); } @Override protected BaseObjectReference createReference() { BaseObjectReference reference; if (getXClassReference() != null && getDocumentReference() != null) { reference = new BaseObjectReference(getXClassReference(), getNumber(), getDocumentReference()); } else { reference = null; } return reference; } @Override public void setNumber(int number) { super.setNumber(number); // Reset reference cache this.referenceCache = null; } @Override public void setXClassReference(EntityReference xClassReference) { super.setXClassReference(xClassReference); // Reset reference cache this.referenceCache = null; } public void displayHidden(StringBuffer buffer, String name, String prefix, XWikiContext context) { ((PropertyClass) getXClass(context).get(name)).displayHidden(buffer, name, prefix, this, context); } public void displayView(StringBuffer buffer, String name, String prefix, XWikiContext context) { ((PropertyClass) getXClass(context).get(name)).displayView(buffer, name, prefix, this, context); } public void displayEdit(StringBuffer buffer, String name, String prefix, XWikiContext context) { ((PropertyClass) getXClass(context).get(name)).displayEdit(buffer, name, prefix, this, context); } public String displayHidden(String name, String prefix, XWikiContext context) { StringBuffer buffer = new StringBuffer(); ((PropertyClass) getXClass(context).get(name)).displayHidden(buffer, name, prefix, this, context); return buffer.toString(); } public String displayView(String name, String prefix, XWikiContext context) { StringBuffer buffer = new StringBuffer(); ((PropertyClass) getXClass(context).get(name)).displayView(buffer, name, prefix, this, context); return buffer.toString(); } public String displayEdit(String name, String prefix, XWikiContext context) { StringBuffer buffer = new StringBuffer(); ((PropertyClass) getXClass(context).get(name)).displayEdit(buffer, name, prefix, this, context); return buffer.toString(); } public String displayHidden(String name, XWikiContext context) { return displayHidden(name, "", context); } public String displayView(String name, XWikiContext context) { return displayView(name, "", context); } public String displayEdit(String name, XWikiContext context) { return displayEdit(name, "", context); } @Override public BaseObject clone() { BaseObject object = (BaseObject) super.clone(); object.setGuid(getGuid()); return object; } /** * Similar to {@link #clone()} but whereas a clone is an exact copy (with the same GUID), a duplicate keeps the same * data but with a different identity. * * @since 2.2.3 */ public BaseObject duplicate() { BaseObject object = clone(); // Reset GUID for the duplicate object.setGuid(null); return object; } /** * @since 2.2.3 */ public BaseObject duplicate(DocumentReference documentReference) { BaseObject object = duplicate(); object.setDocumentReference(documentReference); return object; } @Override public boolean equals(Object obj) { // Same Java object, they sure are equal if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getNumber() != ((BaseObject) obj).getNumber()) { return false; } return true; } @Override public void fromXML(Element oel) throws XWikiException { super.fromXML(oel); } @Override public List<ObjectDiff> getDiff(Object oldEntity, XWikiContext context) { ArrayList<ObjectDiff> difflist = new ArrayList<ObjectDiff>(); BaseObject oldObject = (BaseObject) oldEntity; // Iterate over the new properties first, to handle changed and added objects for (String propertyName : this.getPropertyList()) { BaseProperty newProperty = (BaseProperty) this.getField(propertyName); BaseProperty oldProperty = (BaseProperty) oldObject.getField(propertyName); BaseClass bclass = getXClass(context); PropertyClass pclass = (PropertyClass) ((bclass == null) ? null : bclass.getField(propertyName)); String propertyType = (pclass == null) ? "" : pclass.getClassType(); if (oldProperty == null) { // The property exist in the new object, but not in the old one if ((newProperty != null) && (!newProperty.toText().equals(""))) { String newPropertyValue = (newProperty.getValue() instanceof String || pclass == null) ? newProperty.toText() : pclass.displayView(propertyName, this, context); difflist.add(new ObjectDiff(getXClassReference(), getNumber(), getGuid(), ObjectDiff.ACTION_PROPERTYADDED, propertyName, propertyType, "", newPropertyValue)); } } else if (!oldProperty.toText().equals(((newProperty == null) ? "" : newProperty.toText()))) { // The property exists in both objects and is different if (pclass != null) { // Put the values as they would be displayed in the interface String newPropertyValue = (newProperty.getValue() instanceof String) ? newProperty.toText() : pclass.displayView(propertyName, this, context); String oldPropertyValue = (oldProperty.getValue() instanceof String) ? oldProperty.toText() : pclass.displayView(propertyName, oldObject, context); difflist.add( new ObjectDiff(getXClassReference(), getNumber(), getGuid(), ObjectDiff.ACTION_PROPERTYCHANGED, propertyName, propertyType, oldPropertyValue, newPropertyValue)); } else { // Cannot get property definition, so use the plain value difflist.add( new ObjectDiff(getXClassReference(), getNumber(), getGuid(), ObjectDiff.ACTION_PROPERTYCHANGED, propertyName, propertyType, oldProperty.toText(), newProperty.toText())); } } } // Iterate over the old properties, in case there are some removed properties for (String propertyName : oldObject.getPropertyList()) { BaseProperty newProperty = (BaseProperty) this.getField(propertyName); BaseProperty oldProperty = (BaseProperty) oldObject.getField(propertyName); BaseClass bclass = getXClass(context); PropertyClass pclass = (PropertyClass) ((bclass == null) ? null : bclass.getField(propertyName)); String propertyType = (pclass == null) ? "" : pclass.getClassType(); if (newProperty == null) { // The property exists in the old object, but not in the new one if ((oldProperty != null) && (!oldProperty.toText().equals(""))) { if (pclass != null) { // Put the values as they would be displayed in the interface String oldPropertyValue = (oldProperty.getValue() instanceof String) ? oldProperty.toText() : pclass.displayView(propertyName, oldObject, context); difflist.add( new ObjectDiff(oldObject.getXClassReference(), oldObject.getNumber(), oldObject.getGuid(), ObjectDiff.ACTION_PROPERTYREMOVED, propertyName, propertyType, oldPropertyValue, "")); } else { // Cannot get property definition, so use the plain value difflist.add(new ObjectDiff(oldObject.getXClassReference(), oldObject.getNumber(), oldObject.getGuid(), ObjectDiff.ACTION_PROPERTYREMOVED, propertyName, propertyType, oldProperty.toText(), "")); } } } } return difflist; } public com.xpn.xwiki.api.Object newObjectApi(BaseObject obj, XWikiContext context) { return new com.xpn.xwiki.api.Object(obj, context); } public void set(String fieldname, java.lang.Object value, XWikiContext context) { BaseClass bclass = getXClass(context); PropertyClass pclass = (PropertyClass) bclass.get(fieldname); BaseProperty prop = (BaseProperty) safeget(fieldname); if ((value instanceof String) && (pclass != null)) { prop = pclass.fromString((String) value); } else { if ((prop == null) && (pclass != null)) { prop = pclass.newProperty(); } if (prop != null) { prop.setValue(value); } } if (prop != null) { prop.setOwnerDocument(getOwnerDocument()); safeput(fieldname, prop); } } /** * @return the unique identifier of the object, never null */ public String getGuid() { if (this.guid == null) { return generateGuid(); } return this.guid; } private synchronized String generateGuid() { if (this.guid == null) { this.guid = UUID.randomUUID().toString(); } return this.guid; } /** * @param guid the unique identifier of the object, if null a new one will be generated */ public void setGuid(String guid) { this.guid = guid; } /** * Set the owner document of this base object. * * @param ownerDocument The owner document. * @since 4.3M2 */ @Override public void setOwnerDocument(XWikiDocument ownerDocument) { super.setOwnerDocument(ownerDocument); if (this.ownerDocument != null) { setDocumentReference(this.ownerDocument.getDocumentReference()); } } @Override protected void mergeField(PropertyInterface currentElement, ElementInterface previousElement, ElementInterface newElement, MergeConfiguration configuration, XWikiContext context, MergeResult mergeResult) { BaseClass baseClass = getXClass(context); if (baseClass != null) { PropertyClass propertyClass = (PropertyClass) baseClass.get(currentElement.getName()); if (propertyClass != null) { try { propertyClass.mergeProperty((BaseProperty) currentElement, (BaseProperty) previousElement, (BaseProperty) newElement, configuration, context, mergeResult); } catch (Exception e) { mergeResult.getLog().error("Failed to merge field [{}]", currentElement.getName(), e); } return; } } super.mergeField(currentElement, previousElement, newElement, configuration, context, mergeResult); } }