blob: 2186ece6dfe82ecdb5bdc0fff34ad6fa545c70dc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.scrplugin.om;
import org.apache.felix.scrplugin.Constants;
import org.apache.felix.scrplugin.SCRDescriptorException;
import org.apache.felix.scrplugin.helper.IssueLog;
import org.apache.felix.scrplugin.helper.StringUtils;
import org.apache.felix.scrplugin.tags.*;
/**
* <code>Reference.java</code>...
*
*/
public class Reference extends AbstractObject {
protected String name;
protected String interfacename;
protected String target;
protected String cardinality;
protected String policy;
protected String bind;
protected String unbind;
protected String updated;
/** @since 1.0.9 */
protected String strategy;
/** Is this reference already checked? */
protected boolean checked = false;
/** The class description containing this reference. */
protected final JavaClassDescription javaClassDescription;
/**
* Default constructor.
*/
public Reference() {
this(null, null);
}
/**
* Constructor from java source.
*/
public Reference(JavaTag t, JavaClassDescription desc) {
super(t);
this.javaClassDescription = desc;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getInterfacename() {
return this.interfacename;
}
public void setInterfacename(String interfacename) {
this.interfacename = interfacename;
}
public String getTarget() {
return this.target;
}
public void setTarget(String target) {
this.target = target;
}
public String getCardinality() {
return this.cardinality;
}
public void setCardinality(String cardinality) {
this.cardinality = cardinality;
}
public String getPolicy() {
return this.policy;
}
public void setPolicy(String policy) {
this.policy = policy;
}
public String getBind() {
return this.bind;
}
public void setBind(String bind) {
this.bind = bind;
}
public String getUnbind() {
return this.unbind;
}
public void setUnbind(String unbind) {
this.unbind = unbind;
}
public String getUpdated() {
return this.updated;
}
public void setUpdated(String updated) {
this.updated = updated;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
/** @since 1.0.9 */
public String getStrategy() {
return strategy;
}
/** @since 1.0.9 */
public void setStrategy(String strategy) {
this.strategy = strategy;
}
/** @since 1.0.9 */
public boolean isLookupStrategy() {
return Constants.REFERENCE_STRATEGY_LOOKUP.equals(getStrategy());
}
/**
* Validate the property.
* If errors occur a message is added to the issues list,
* warnings can be added to the warnings list.
*/
public void validate(final int specVersion,
final boolean componentIsAbstract,
final IssueLog iLog)
throws SCRDescriptorException {
// if this reference is already checked, return immediately
if ( this.checked ) {
return;
}
final int currentIssueCount = iLog.getNumberOfErrors();
// validate name
if (StringUtils.isEmpty(this.name)) {
if ( specVersion < Constants.VERSION_1_1 ) {
this.logError( iLog, "Reference has no name" );
}
}
// validate interface
if (StringUtils.isEmpty(this.interfacename)) {
this.logError( iLog, "Missing interface name" );
}
// validate cardinality
if (this.cardinality == null) {
this.cardinality = "1..1";
} else if (!"0..1".equals(this.cardinality) && !"1..1".equals(this.cardinality)
&& !"0..n".equals(this.cardinality) && !"1..n".equals(this.cardinality)) {
this.logError( iLog, "Invalid Cardinality specification " + this.cardinality );
}
// validate policy
if (this.policy == null) {
this.policy = "static";
} else if (!"static".equals(this.policy) && !"dynamic".equals(this.policy)) {
this.logError( iLog, "Invalid Policy specification " + this.policy );
}
// validate strategy
if (this.strategy == null) {
this.strategy = Constants.REFERENCE_STRATEGY_EVENT;
} else if (!Constants.REFERENCE_STRATEGY_EVENT.equals(this.strategy)
&& !Constants.REFERENCE_STRATEGY_LOOKUP.equals(this.strategy)) {
this.logError( iLog, "Invalid strategy type " + this.strategy );
}
// validate bind and unbind methods
if (!isLookupStrategy()) {
// set default values
if ( this.bind == null ) {
this.setBind("bind");
}
if ( this.unbind == null ) {
this.setUnbind("unbind");
}
final String oldBind = this.bind;
final String oldUnbind = this.unbind;
this.bind = this.validateMethod(specVersion, this.bind, componentIsAbstract, iLog);
this.unbind = this.validateMethod(specVersion, this.unbind, componentIsAbstract, iLog);
if ( iLog.getNumberOfErrors() == currentIssueCount ) {
if ( this.bind != null && this.unbind != null ) {
// no errors, so we're checked
this.checked = true;
} else {
if ( this.bind == null ) {
this.bind = oldBind;
}
if ( this.unbind == null ) {
this.unbind = oldUnbind;
}
}
}
} else {
this.bind = null;
this.unbind = null;
}
// validate updated method
if ( this.updated != null ) {
if ( specVersion < Constants.VERSION_1_1_FELIX ) {
this.logError( iLog, "Updated method declaration requires namespace "
+ Constants.COMPONENT_DS_SPEC_VERSION_11_FELIX + " or newer" );
}
}
}
protected String validateMethod(final int specVersion,
final String methodName,
final boolean componentIsAbstract,
final IssueLog iLog)
throws SCRDescriptorException {
final JavaMethod method = this.findMethod(specVersion, methodName);
if (method == null) {
if ( !componentIsAbstract ) {
this.logError( iLog, "Missing method " + methodName + " for reference " + (this.getName() == null ? "" : this.getName()));
}
return null;
}
// method needs to be protected for 1.0
if ( specVersion == Constants.VERSION_1_0 ) {
if (method.isPublic()) {
this.logWarn( iLog, "Method " + method.getName() + " should be declared protected" );
} else if (!method.isProtected()) {
this.logError( iLog, "Method " + method.getName() + " has wrong qualifier, public or protected required" );
return null;
}
}
return method.getName();
}
private static final String TYPE_SERVICE_REFERENCE = "org.osgi.framework.ServiceReference";
private static final String TYPE_MAP = "java.util.Map";
public JavaMethod findMethod(final int specVersion,
final String methodName)
throws SCRDescriptorException {
final String[] sig = new String[]{ TYPE_SERVICE_REFERENCE };
final String[] sig2 = new String[]{ this.getInterfacename() };
final String[] sig3 = new String[]{ this.getInterfacename(), TYPE_MAP};
// service interface or ServiceReference first
String realMethodName = methodName;
JavaMethod method = this.javaClassDescription.getMethodBySignature(realMethodName, sig);
if (method == null) {
method = this.javaClassDescription.getMethodBySignature(realMethodName, sig2);
if ( specVersion >= Constants.VERSION_1_1 && method == null ) {
method = this.javaClassDescription.getMethodBySignature(realMethodName, sig3);
}
}
// append reference name with service interface and ServiceReference
if (method == null) {
final String info;
if (StringUtils.isEmpty(this.name)) {
final String interfaceName = this.getInterfacename();
final int pos = interfaceName.lastIndexOf('.');
info = interfaceName.substring(pos + 1);
} else {
info = this.name;
}
realMethodName = methodName + Character.toUpperCase(info.charAt(0)) + info.substring(1);
method = this.javaClassDescription.getMethodBySignature(realMethodName, sig);
}
if (method == null) {
method = this.javaClassDescription.getMethodBySignature(realMethodName, sig2);
if ( specVersion >= Constants.VERSION_1_1 && method == null ) {
method = this.javaClassDescription.getMethodBySignature(realMethodName, sig3);
}
}
// append type name with service interface and ServiceReference
if (method == null) {
int lastDot = this.getInterfacename().lastIndexOf('.');
realMethodName = methodName
+ this.getInterfacename().substring(lastDot + 1);
method = this.javaClassDescription.getMethodBySignature(realMethodName, sig);
}
if (method == null) {
method = this.javaClassDescription.getMethodBySignature(realMethodName, sig2);
if ( specVersion >= Constants.VERSION_1_1 && method == null ) {
method = this.javaClassDescription.getMethodBySignature(realMethodName, sig3);
}
}
return method;
}
}