001/*
002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Kevin Leturc
018 */
019package org.nuxeo.ecm.core.api.model.impl;
020
021import java.io.Serializable;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.Iterator;
025import java.util.Objects;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.core.api.NuxeoException;
030import org.nuxeo.ecm.core.api.PropertyException;
031import org.nuxeo.ecm.core.api.model.Property;
032import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
033import org.nuxeo.ecm.core.api.model.PropertyVisitor;
034import org.nuxeo.ecm.core.schema.types.ComplexTypeImpl;
035import org.nuxeo.ecm.core.schema.types.Field;
036import org.nuxeo.ecm.core.schema.types.Type;
037
038/**
039 * Property used to declare property removed from schema.
040 *
041 * @since 9.2
042 */
043public class RemovedProperty extends AbstractProperty {
044
045    private static final long serialVersionUID = 1L;
046
047    private static final Log log = LogFactory.getLog(RemovedProperty.class);
048
049    protected final String fieldName;
050
051    protected final Property fallback;
052
053    public RemovedProperty(Property parent, String fieldName) {
054        this(parent, fieldName, null);
055    }
056
057    public RemovedProperty(Property parent, String fieldName, Property fallback) {
058        super(parent);
059        this.fieldName = fieldName;
060        this.fallback = fallback;
061    }
062
063    @Override
064    public void internalSetValue(Serializable value) throws PropertyException {
065        StringBuilder msg = newRemovedMessage();
066        if (fallback == null) {
067            msg.append("Do nothing");
068        } else {
069            msg.append("Set value to fallback property '").append(fallback.getXPath()).append("'");
070        }
071        if (log.isTraceEnabled()) {
072            log.error(msg, new NuxeoException("debug stack trace"));
073        } else {
074            log.error(msg);
075        }
076        if (fallback != null) {
077            fallback.setValue(value);
078        }
079    }
080
081    @Override
082    public Serializable internalGetValue() throws PropertyException {
083        StringBuilder msg = newRemovedMessage();
084        if (fallback == null) {
085            msg.append("Return null");
086        } else {
087            msg.append("Return value from '").append(fallback.getXPath()).append("'");
088        }
089        if (log.isTraceEnabled()) {
090            log.error(msg, new NuxeoException());
091        } else {
092            log.error(msg);
093        }
094        if (fallback == null) {
095            return null;
096        }
097        return fallback.getValue();
098    }
099
100    protected StringBuilder newRemovedMessage() {
101        StringBuilder builder = new StringBuilder().append("Property '")
102                                                   .append(getXPath())
103                                                   .append("' is marked as removed from '")
104                                                   .append(getSchema().getName())
105                                                   .append("' schema");
106        RemovedProperty removedParent = getRemovedParent();
107        if (removedParent != this) {
108            builder.append(" because property '").append(removedParent.getXPath()).append("' is marked as removed");
109        }
110        return builder.append(", don't use it anymore. ");
111    }
112
113    protected RemovedProperty getRemovedParent() {
114        RemovedProperty property = this;
115        while (property.getParent() instanceof RemovedProperty) {
116            property = (RemovedProperty) property.getParent();
117        }
118        return property;
119    }
120
121    @Override
122    public String getName() {
123        return fieldName;
124    }
125
126    @Override
127    public Type getType() {
128        if (fallback == null) {
129            // TODO try to do something better - currently RemovedProperty is always a container if there's no fallback
130            // Simulate a complex type
131            return new ComplexTypeImpl(getSchema(), getSchema().getName(), fieldName);
132        }
133        return fallback.getType();
134    }
135
136    @Override
137    public boolean isContainer() {
138        // TODO try to do something better - currently RemovedProperty is always a container if there's no fallback
139        return fallback == null || fallback.isContainer();
140    }
141
142    @Override
143    public Collection<Property> getChildren() {
144        return Collections.emptyList();
145    }
146
147    @Override
148    public Property get(String name) throws PropertyNotFoundException {
149        if (fallback != null) {
150            return new RemovedProperty(this, name, fallback.get(name));
151        } else if (name.matches("\\d+")) {
152            return get(Integer.parseInt(name));
153        }
154        return new RemovedProperty(this, name);
155    }
156
157    @Override
158    public Property get(int index) throws PropertyNotFoundException {
159        // TODO try to do something better - currently RemovedProperty is always a container if there's no fallback
160        return new RemovedProperty(this, fieldName);
161    }
162
163    @Override
164    public Property addValue(Object value) throws PropertyException {
165        throw new UnsupportedOperationException("Removed properties don't have children");
166    }
167
168    @Override
169    public Property addValue(int index, Object value) throws PropertyException {
170        throw new UnsupportedOperationException("Removed properties don't have children");
171    }
172
173    @Override
174    public Property addEmpty() throws PropertyException {
175        throw new UnsupportedOperationException("Removed properties don't have children");
176    }
177
178    @Override
179    public Field getField() {
180        throw new UnsupportedOperationException("Removed properties don't have field");
181    }
182
183    @Override
184    public void accept(PropertyVisitor visitor, Object arg) throws PropertyException {
185        // Nothing to do
186    }
187
188    @Override
189    public boolean isSameAs(Property property) throws PropertyException {
190        if (!(property instanceof RemovedProperty)) {
191            return false;
192        }
193        RemovedProperty rp = (RemovedProperty) property;
194        return Objects.equals(getSchema(), rp.getSchema()) && Objects.equals(getName(), getName())
195                && Objects.equals(getRemovedParent().getName(), rp.getRemovedParent().getName());
196    }
197
198    @Override
199    public Iterator<Property> getDirtyChildren() {
200        return Collections.emptyIterator();
201    }
202
203}