/**
* EasySOA Registry
* Copyright 2012-2013 Open Wide
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
*
* Contact : easysoa-dev@googlegroups.com
*/
package org.easysoa.registry.indicators.rest;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.easysoa.registry.DocumentService;
import org.easysoa.registry.types.InformationService;
import org.easysoa.registry.types.OperationInformation;
import org.easysoa.registry.types.ServiceImplementation;
import org.easysoa.registry.utils.NXQLQueryHelper;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.runtime.api.Framework;
/**
* Computes various indicators on services implementations, including doc quality
*
* @author mkalam-alami; mdutoo
*/
public class ServiceImplStateProvider extends IndicatorProviderBase {
private static Logger logger = Logger.getLogger(ServiceImplStateProvider.class);
//private static final String SERVICEIMPL_DOCTYPE_INDICATOR = DoctypeCountProvider.getName(ServiceImplementation.DOCTYPE);
// TODO : Add a key and a name in indicators
private static final String SERVICEIMPL_DOCTYPE_INDICATOR = DoctypeCountProvider.buildName(ServiceImplementation.DOCTYPE);
private static final String SERVICE_DOCTYPE_INDICATOR = DoctypeCountProvider.buildName(InformationService.DOCTYPE);
/**
*
* @param category
*/
public ServiceImplStateProvider(String category){
super(category);
}
@Override
public List<String> getRequiredIndicators() {
return Arrays.asList(SERVICEIMPL_DOCTYPE_INDICATOR);
}
@Override
public Map<String, IndicatorValue> computeIndicators(CoreSession session, String subprojectId,
Map<String, IndicatorValue> computedIndicators, String visibility) throws Exception {
Map<String, IndicatorValue> indicators = new HashMap<String, IndicatorValue>();
DocumentService documentService = Framework.getService(DocumentService.class);
String subprojectCriteria = NXQLQueryHelper.buildSubprojectCriteria(session, subprojectId, visibility);
// Count indicators - ServiceImplementation-specific
final int IDEAL_DOCUMENTATION_LINES = 40, DOCUMENTATION_LINES_TOLERANCE = 20;
/**
* 1 unit = 40 characters (half standard 80 character line)
* Ideal doc => 4 units for a class
* and for each operations 2 units + 1 unit for each parameter ....
*
*/
final int DOC_LINE_CHARACTERS_NUMBER_UNIT = 40;
int serviceimplsMinIdealDocUnitTotal = 0; // sum of each class' minimum number of doc units to be considered ideally doc'd
int serviceImplsRealDocUnitTotal = 0;
int serviceImplsUnitDocQuality = 0;
int undocumentedServiceImpls = 0, documentationLines = 0;
long serviceImplCount = computedIndicators.get(SERVICEIMPL_DOCTYPE_INDICATOR).getCount();
long serviceCount = computedIndicators.get(SERVICE_DOCTYPE_INDICATOR).getCount();
int serviceImplsDocQuality = 0;
Map<Serializable, Boolean> hasMock = new HashMap<Serializable, Boolean>();
int mockedImplsCount = 0, testedImplsCount = 0, nonMockImplsCount = 0;
// Get service impls
DocumentModelList serviceImplModels = session.query(DocumentService.NXQL_SELECT_FROM
+ ServiceImplementation.DOCTYPE + DocumentService.NXQL_WHERE_NO_PROXY + subprojectCriteria);
for (DocumentModel serviceImplModel : serviceImplModels) {
ServiceImplementation serviceImpl = serviceImplModel.getAdapter(ServiceImplementation.class);
if (!serviceImpl.isPlaceholder()) {
int idealMinDocUnitNumber;
int realDocUnitNumber;
// Documentation info
String documentation = (String) serviceImpl.getProperty(ServiceImplementation.XPATH_DOCUMENTATION);
if (documentation != null && !documentation.isEmpty()) {
documentationLines += computeLines(documentation);
idealMinDocUnitNumber = 4; // For a class => documentation must be at least equivalent to 4 doc units
realDocUnitNumber = Math.round(documentation.length() / DOC_LINE_CHARACTERS_NUMBER_UNIT);
// Operation documentation
for (OperationInformation operation : serviceImpl.getOperations()) {
String operationDocumentation = operation.getDocumentation();
documentationLines += computeLines(operationDocumentation);
idealMinDocUnitNumber += 2; // For a method, at least 2 doc units
String parameters[] = operation.getParameters().split(",");
//totalNbOfParameters += parameters.length; // WARNING not necessarily meaningful,
// ex. ContactSvcSoap.client(name, email, age...) vs PrecomptePartenaireWebService.creerPrecompte(CreerPrecompte) means the same
idealMinDocUnitNumber += parameters.length; // 1 doc unit for each parameter
realDocUnitNumber += Math.round(operationDocumentation.length() / DOC_LINE_CHARACTERS_NUMBER_UNIT);
}
// computing doc quality : = 20 if in the tolerance range, otherwise drops down to 0 beyond twice the tolerance range
int serviceImplDocQuality = Math.max(0,
IDEAL_DOCUMENTATION_LINES - DOCUMENTATION_LINES_TOLERANCE
- Math.max(0,
Math.abs(IDEAL_DOCUMENTATION_LINES - documentationLines)
- DOCUMENTATION_LINES_TOLERANCE)); // 0 => 0, 10 => 10, 20 => 20 same up to 60 => 20, 70 => 10, 80 => 0, 100 => -20
logger.debug("Doc quality for service impl " + serviceImpl.getName() + " : " + serviceImplDocQuality);
serviceImplsDocQuality += serviceImplDocQuality;
serviceImplsRealDocUnitTotal += realDocUnitNumber;
serviceimplsMinIdealDocUnitTotal += idealMinDocUnitNumber;
int serviceImplUnitDocQuality = Math.round(100 * realDocUnitNumber / idealMinDocUnitNumber);
if(serviceImplUnitDocQuality > 100){
serviceImplUnitDocQuality = 100; // Set to the max % value if there is too much doc in the class
}
serviceImplsUnitDocQuality += serviceImplUnitDocQuality;
logger.debug("Doc units quality for service impl " + serviceImpl.getName() + " : " + serviceImplUnitDocQuality + "%");
logger.debug("Ideal Doc units for service impl " + serviceImpl.getName() + " : " + idealMinDocUnitNumber);
logger.debug("Real Doc units for service impl " + serviceImpl.getName() + " : " + realDocUnitNumber);
}
else {
undocumentedServiceImpls++;
}
// Mock info
String parentServiceId = null;
DocumentModelList implParents = documentService.findAllParents(session, serviceImplModel);
for (DocumentModel implParent : implParents) {
if (InformationService.DOCTYPE.equals(implParent.getType())) {
parentServiceId = (String) implParent.getPropertyValue(InformationService.XPATH_SOANAME);
break;
}
}
if (parentServiceId != null) {
if (serviceImpl.isMock()) {
hasMock.put(parentServiceId, true);
}
else if (!hasMock.containsKey(parentServiceId)) {
hasMock.put(parentServiceId, false);
}
}
if (!serviceImpl.isMock()) {
nonMockImplsCount++;
// Tests info
if (!serviceImpl.getTests().isEmpty()) {
testedImplsCount++;
}
}
}
}
for (Boolean isMock : hasMock.values()) {
if (isMock) {
mockedImplsCount++;
}
}
// Indicators results registration
newIndicator(indicators, "serviceImplementationWithoutDocumentation", undocumentedServiceImpls, serviceImplCount);
newIndicator(indicators, "serviceImplementationDocumentationLineAverage", (serviceImplCount - undocumentedServiceImpls > 0) ? (int) (documentationLines / (serviceImplCount - undocumentedServiceImpls)) : -1, -1);
newIndicator(indicators, "serviceImplementationWithoutMock", nonMockImplsCount - mockedImplsCount, nonMockImplsCount);
newIndicator(indicators, "serviceImplementationWithoutTest", nonMockImplsCount - testedImplsCount, nonMockImplsCount);
// Obsolete indicator with old formula
//newIndicator(indicators, "documentedServiceImplementationDocumentationQuality", (serviceImplCount - undocumentedServiceImpls > 0) ?
// (int) (100 * serviceImplsDocQuality / ((IDEAL_DOCUMENTATION_LINES - DOCUMENTATION_LINES_TOLERANCE) * (serviceImplCount - undocumentedServiceImpls))) : 0);
// New indicator with doc unit formula
newIndicator(indicators, "documentedServiceImplementationDocumentationQuality", (serviceImplCount - undocumentedServiceImpls > 0) ?
(int) (serviceImplsUnitDocQuality / (serviceImplCount - undocumentedServiceImpls)) : 0);
// TODO model consistency ex. impl without service
// TODO for one ex. impl of ONE service => prop to query
return indicators;
}
private int computeLines(String documentation) {
if(documentation != null){
return documentation.split("\n").length;
}
return 0;
}
}