/*
* Copyright 2004-2014 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.rest;
import com.eviware.soapui.config.OldRestRequestConfig;
import com.eviware.soapui.config.RestMethodConfig;
import com.eviware.soapui.config.RestResourceConfig;
import com.eviware.soapui.impl.rest.support.RestParamProperty;
import com.eviware.soapui.impl.rest.support.RestParamsPropertyHolder;
import com.eviware.soapui.impl.rest.support.RestRequestConverter;
import com.eviware.soapui.impl.rest.support.XmlBeansRestParamsTestPropertyHolder;
import com.eviware.soapui.impl.support.AbstractHttpOperation;
import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
import com.eviware.soapui.impl.wsdl.MutableTestPropertyHolder;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.iface.Attachment.AttachmentEncoding;
import com.eviware.soapui.model.iface.MessagePart;
import com.eviware.soapui.model.iface.Request;
import com.eviware.soapui.model.propertyexpansion.PropertyExpansionContext;
import com.eviware.soapui.model.testsuite.TestProperty;
import com.eviware.soapui.model.testsuite.TestPropertyListener;
import com.eviware.soapui.support.StringUtils;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
/**
* WSDL implementation of Operation, maps to a WSDL BindingOperation
*
* @author Ole.Matzura
*/
public class RestResource extends AbstractWsdlModelItem<RestResourceConfig> implements AbstractHttpOperation,
MutableTestPropertyHolder, RestResourceContainer, PropertyChangeListener {
public static final String PATH_PROPERTY = "path";
private List<RestMethod> methods = new ArrayList<RestMethod>();
private List<RestResource> resources = new ArrayList<RestResource>();
private RestResource parentResource;
private XmlBeansRestParamsTestPropertyHolder params;
private PropertyChangeListener styleChangeListener = new StyleChangeListener();
public RestResource(RestService service, RestResourceConfig resourceConfig) {
this(service, null, resourceConfig);
}
public RestResource(RestResource restResource, RestResourceConfig resourceConfig) {
this(restResource.getInterface(), restResource, resourceConfig);
parentResource.addPropertyChangeListener(this);
}
private RestResource(RestService service, RestResource parent, RestResourceConfig resourceConfig) {
super(resourceConfig, service, "/rest_resource.gif");
parentResource = parent;
if (resourceConfig.getParameters() == null) {
resourceConfig.addNewParameters();
}
params = new XmlBeansRestParamsTestPropertyHolder(this, resourceConfig.getParameters());
params.addTestPropertyListener(new PathChanger());
for (String name : params.getPropertyNames()) {
params.getProperty(name).addPropertyChangeListener(new StyleChangeListener());
}
for (RestMethodConfig config : resourceConfig.getMethodList()) {
methods.add(new RestMethod(this, config));
}
for (RestResourceConfig config : resourceConfig.getResourceList()) {
resources.add(new RestResource(this, config));
}
for (OldRestRequestConfig config : resourceConfig.getRequestList()) {
RestRequestConverter.convert(this, config);
}
resourceConfig.setRequestArray(new OldRestRequestConfig[]{});
service.addPropertyChangeListener(this);
}
public RestResource getParentResource() {
return parentResource;
}
public RestResourceContainer getResourceContainer() {
return parentResource == null ? getInterface() : parentResource;
}
@Override
public List<? extends ModelItem> getChildren() {
List<ModelItem> result = new ArrayList<ModelItem>();
result.addAll(getRestMethodList());
result.addAll(getChildResourceList());
return result;
}
public MessagePart[] getDefaultRequestParts() {
return new MessagePart[0];
}
public MessagePart[] getDefaultResponseParts() {
return new MessagePart[0];
}
public RestService getInterface() {
return (RestService) getParent();
}
public String[] getRequestMediaTypes() {
return new String[0];
}
public String[] getResponseMediaTypes() {
return new String[0];
}
public RestResource getChildResourceByName(String name) {
return (RestResource) getWsdlModelItemByName(resources, name);
}
public RestResource addNewChildResource(String name, String path) {
RestResourceConfig resourceConfig = getConfig().addNewResource();
resourceConfig.setName(name);
resourceConfig.setPath(path);
RestResource resource = new RestResource(this, resourceConfig);
resources.add(resource);
getInterface().fireOperationAdded(resource);
notifyPropertyChanged("childResources", null, resource);
return resource;
}
public int getChildResourceCount() {
return resources.size();
}
public List<RestResource> getChildResourceList() {
return new ArrayList<RestResource>(resources);
}
public RestRequest getRequestAt(int index) {
for (RestMethod m : methods) {
if (index < m.getRequestCount()) {
return m.getRequestAt(index);
} else {
index -= m.getRequestCount();
}
}
throw new IndexOutOfBoundsException();
}
public RestRequest getRequestByName(String name) {
for (RestMethod m : methods) {
RestRequest r = m.getRequestByName(name);
if (r != null) {
return r;
}
}
return null;
}
public RestMethod addNewMethod(String name) {
RestMethodConfig methodConfig = getConfig().addNewMethod();
methodConfig.setName(name);
RestMethod method = new RestMethod(this, methodConfig);
/*
* for (RestParamProperty prop : getDefaultParams()) { if
* (!method.hasProperty(prop.getName()))
* method.addProperty(prop.getName()).setValue(prop.getDefaultValue()); }
*/
methods.add(method);
notifyPropertyChanged("childMethods", null, method);
return method;
}
public int getRestMethodCount() {
return methods.size();
}
public List<RestMethod> getRestMethodList() {
return new ArrayList<RestMethod>(methods);
}
public RestMethod getRestMethodByName(String name) {
return (RestMethod) getWsdlModelItemByName(methods, name);
}
public int getRequestCount() {
int size = 0;
for (RestMethod m : methods) {
size += m.getRequestCount();
}
return size;
}
public List<Request> getRequestList() {
List<Request> rs = new ArrayList<Request>();
for (RestMethod m : methods) {
rs.addAll(m.getRequestList());
}
return rs;
}
public String getPath() {
String path = getConfig().getPath();
// A bug introduced in 4.6.0 appended matrix param in the resource path, so projects created with 4.6.0 will save
// matrix param in the path. Following line takes away matrix param from the path.
return removeMatrixParams(path);
}
public void setPath(String path) {
String old = getPath();
getConfig().setPath(path);
notifyPropertyChanged("path", old, path);
}
public boolean isBidirectional() {
return true;
}
public boolean isNotification() {
return false;
}
public boolean isOneWay() {
return false;
}
public boolean isRequestResponse() {
return true;
}
public boolean isSolicitResponse() {
return false;
}
public boolean isUnidirectional() {
return false;
}
public AttachmentEncoding getAttachmentEncoding(String part, boolean isRequest) {
return AttachmentEncoding.NONE;
}
public RestParamProperty[] getDefaultParams() {
List<RestParamProperty> result = new ArrayList<RestParamProperty>();
Set<String> names = new HashSet<String>();
if (parentResource != null) {
result.addAll(Arrays.asList(parentResource.getDefaultParams()));
}
for (int c = 0; c < getPropertyCount(); c++) {
if (names.contains(getPropertyAt(c).getName())) {
continue;
}
result.add(getPropertyAt(c));
names.add(getPropertyAt(c).getName());
}
return result.toArray(new RestParamProperty[result.size()]);
}
public String getFullPath() {
return getFullPath(true);
}
public String getFullPath(boolean includeBasePath) {
String base = parentResource == null ? (includeBasePath ? getInterface().getBasePath() : "") : parentResource
.getFullPath(includeBasePath);
String path = getPath();
if (StringUtils.hasContent(path) && base != null && !base.endsWith("/") && !path.startsWith("/")) {
base += "/";
}
return path == null ? base : base + path;
}
public RestParamProperty addProperty(String name) {
return params.addProperty(name);
}
public void moveProperty(String propertyName, int targetIndex) {
params.moveProperty(propertyName, targetIndex);
}
public RestParamProperty removeProperty(String propertyName) {
return params.removeProperty(propertyName);
}
public boolean renameProperty(String name, String newName) {
if (hasProperty(name)) {
return params.renameProperty(name, newName);
} else if (parentResource != null) {
return parentResource.renameProperty(name, newName);
} else {
return false;
}
}
public void addTestPropertyListener(TestPropertyListener listener) {
params.addTestPropertyListener(listener);
}
public RestParamsPropertyHolder getParams() {
return params;
}
public ModelItem getModelItem() {
return this;
}
public Map<String, TestProperty> getProperties() {
return params.getProperties();
}
public RestParamProperty getProperty(String name) {
return params.getProperty(name);
}
public RestParamProperty getPropertyAt(int index) {
return params.getPropertyAt(index);
}
public int getPropertyCount() {
return params.getPropertyCount();
}
public String[] getPropertyNames() {
return params.getPropertyNames();
}
public String getPropertyValue(String name) {
return params.getPropertyValue(name);
}
public boolean hasProperty(String name) {
return params.hasProperty(name);
}
public void removeTestPropertyListener(TestPropertyListener listener) {
params.removeTestPropertyListener(listener);
}
public void setPropertyValue(String name, String value) {
params.setPropertyValue(name, value);
}
public String getPropertiesLabel() {
return "Resource Params";
}
public String buildPath(PropertyExpansionContext context) {
return getFullPath(true);
}
public RestResource cloneChildResource(RestResource resource, String name) {
return cloneResource(resource, name);
}
public RestResource cloneResource(RestResource resource, String name) {
RestResourceConfig resourceConfig = (RestResourceConfig) getConfig().addNewResource().set(resource.getConfig());
resourceConfig.setName(name);
RestResource newResource = new RestResource(this, resourceConfig);
resources.add(newResource);
getInterface().fireOperationAdded(newResource);
return newResource;
}
public RestMethod cloneMethod(RestMethod method, String name) {
RestMethodConfig methodConfig = (RestMethodConfig) getConfig().addNewMethod().set(method.getConfig());
methodConfig.setName(name);
RestMethod newMethod = new RestMethod(this, methodConfig);
methods.add(newMethod);
notifyPropertyChanged("childMethods", null, newMethod);
return newMethod;
}
@Override
public void release() {
super.release();
params.release();
getService().removePropertyChangeListener(this);
if (parentResource != null) {
parentResource.removePropertyChangeListener(this);
}
for (RestResource resource : resources) {
resource.release();
}
for (RestMethod method : methods) {
method.release();
}
}
public void deleteMethod(RestMethod method) {
int ix = methods.indexOf(method);
if (!methods.remove(method)) {
return;
}
notifyPropertyChanged("childMethods", method, null);
method.release();
getConfig().removeMethod(ix);
}
protected void deleteAllChildResources(RestResource resource) {
for (RestResource childResource : resource.getChildResourceList()) {
resource.deleteResource(childResource);
}
}
public void deleteResource(RestResource resource) {
deleteAllChildResources(resource);
int ix = resources.indexOf(resource);
if (!resources.remove(resource)) {
return;
}
getInterface().fireOperationRemoved(resource);
notifyPropertyChanged("childResources", resource, null);
getConfig().removeResource(ix);
resource.release();
}
public String createRequest(boolean b) {
return null;
}
public String createResponse(boolean b) {
return null;
}
public RestResource getChildResourceAt(int c) {
return resources.get(c);
}
public RestMethod getRestMethodAt(int c) {
return methods.get(c);
}
public RestService getService() {
return (RestService) (getParentResource() == null ? getParent() : getParentResource().getService());
}
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("path") || evt.getPropertyName().equals("basePath")) {
notifyPropertyChanged("path", null, getPath());
}
}
public RestResource[] getAllChildResources() {
List<RestResource> result = new ArrayList<RestResource>();
for (RestResource resource : resources) {
addResourcesToResult(resource, result);
}
return result.toArray(new RestResource[result.size()]);
}
private void addResourcesToResult(RestResource resource, List<RestResource> result) {
result.add(resource);
for (RestResource res : resource.getChildResourceList()) {
addResourcesToResult(res, result);
}
}
public List<TestProperty> getPropertyList() {
return params.getPropertyList();
}
//Helper methods
public static String removeMatrixParams(String path) {
if (path == null || path.isEmpty()) {
return path;
}
return path.replaceAll("(\\;).+(\\=).*(?!\\/)", "");
}
public RestResource getTopLevelResource() {
if (getParentResource() == null) {
return this;
} else {
return getParentResource().getTopLevelResource();
}
}
private class PathChanger implements TestPropertyListener {
@Override
public void propertyAdded(String name) {
params.getProperty(name).addPropertyChangeListener(styleChangeListener);
}
@Override
public void propertyRemoved(String name) {
if (doesParameterExist(name) && isTemplateProperty(name)) {
setPath(getPath().replaceAll("\\{" + name + "\\}", ""));
}
}
private boolean doesParameterExist(String name) {
RestParamProperty property = params.getProperty(name);
return property != null;
}
private boolean isTemplateProperty(String name) {
RestParamProperty property = params.getProperty(name);
return property != null && property.getStyle() == RestParamsPropertyHolder.ParameterStyle.TEMPLATE;
}
@Override
public void propertyRenamed(String oldName, String newName) {
if (isTemplateProperty(newName)) // Since the property is already renamed, we look for the new name
{
setPath(getPath().replaceAll("\\{" + oldName + "\\}", "\\{" + newName + "\\}"));
}
}
@Override
public void propertyValueChanged(String name, String oldValue, String newValue) {
}
@Override
public void propertyMoved(String name, int oldIndex, int newIndex) {
}
}
@Override
public String toString() {
return "RestResource: " + getFullPath();
}
private class StyleChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(XmlBeansRestParamsTestPropertyHolder.PROPERTY_STYLE) && getPath() != null) {
String name = ((RestParamProperty) evt.getSource()).getName();
if (evt.getOldValue() == RestParamsPropertyHolder.ParameterStyle.TEMPLATE) {
setPath(getPath().replaceAll("\\{" + name + "\\}", ""));
} else if (evt.getNewValue() == RestParamsPropertyHolder.ParameterStyle.TEMPLATE && !getFullPath().contains("{" + name + "}")) {
setPath(getPath() + "{" + name + "}");
}
}
}
}
}