/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * Licensed 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 com.alibaba.citrus.springext.impl; import static com.alibaba.citrus.springext.Schema.*; import static com.alibaba.citrus.springext.support.SchemaUtil.*; import static com.alibaba.citrus.util.Assert.*; import static com.alibaba.citrus.util.CollectionUtil.*; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.alibaba.citrus.springext.ConfigurationPoint; import com.alibaba.citrus.springext.ConfigurationPointException; import com.alibaba.citrus.springext.Contribution; import com.alibaba.citrus.springext.ContributionType; import com.alibaba.citrus.springext.ResourceResolver.Resource; import com.alibaba.citrus.springext.Schema; import com.alibaba.citrus.springext.SourceInfo; import com.alibaba.citrus.springext.VersionableSchemas; import com.alibaba.citrus.springext.support.ConfigurationPointSourceInfo; import com.alibaba.citrus.springext.support.ContributionSourceInfo; import com.alibaba.citrus.springext.support.SourceInfoSupport; import com.alibaba.citrus.util.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 实现<code>Contribution</code>接口。 * * @author Michael Zhou */ public class ContributionImpl implements Contribution, ContributionSourceInfo { private final static Logger log = LoggerFactory.getLogger(Contribution.class); private final ConfigurationPoint configurationPoint; private final ConfigurationPointSettings settings; private final ContributionKey key; private final String implementationClassName; private final SourceInfo<ConfigurationPointSourceInfo> sourceInfo; private VersionableSchemas schemas; ContributionImpl(ConfigurationPointImpl cp, ConfigurationPointSettings settings, ContributionType type, String name, String contributionClassName, SourceInfo<ConfigurationPointSourceInfo> sourceInfo) { this.configurationPoint = assertNotNull(cp, "configurationPoint"); this.settings = settings; this.key = new ContributionKey(name, type); this.implementationClassName = contributionClassName; // 可能为空,但推迟到创建时再报错 this.sourceInfo = assertNotNull(sourceInfo, "sourceInfo"); } public ConfigurationPoint getConfigurationPoint() { return configurationPoint; } public ContributionType getType() { return key.getType(); } public String getName() { return key.getName(); } public String getAnnotation() { Schema schema = getSchemas().getMainSchema(); if (schema != null) { Element element = schema.getElement(getName()); if (element != null) { return element.getAnnotation(); } } return null; } ContributionKey getKey() { return key; } public String getImplementationClassName() { return implementationClassName; } public VersionableSchemas getSchemas() { if (schemas == null) { String mainName = configurationPoint.getName() + "/" + getName(); // configurationPointName/contributionName Schema mainSchema = loadMainSchema(mainName); Schema[] versionedSchemas = loadVersionedSchemas(mainName); schemas = new VersionableSchemasImpl(mainSchema, versionedSchemas); } return schemas; } private Schema loadMainSchema(String mainName) { String schemaName = mainName + "." + XML_SCHEMA_EXTENSION; Resource resource = settings.getResourceFromRelativeLocation(schemaName, log); if (resource == null) { return null; // no schema found } else { log.debug("Found schema file for contribution {}: {}", mainName, resource); return SchemaImpl.createForContribution( schemaName, null, getDescription(), resource, new SourceInfoSupport<ContributionSourceInfo>(this).setSource(resource), // 此方法有一个副作用,将会把当前contribution添加到它所依赖的configuration point的depending contributions列表中。 getContributionSchemaTransformer(getConfigurationPoint().getConfigurationPoints(), this), settings.resourceResolver); } } private Schema[] loadVersionedSchemas(String mainName) { String schemaNamePattern = mainName + "-*." + XML_SCHEMA_EXTENSION; Pattern pattern = Pattern.compile("^.*(" + mainName + "-(.+)\\." + XML_SCHEMA_EXTENSION + ")$"); List<Resource> resources = settings.getResourcesFromRelativeLocationPattern(schemaNamePattern, log); assertNotNull(resources, schemaNamePattern); List<Schema> schemas = createLinkedList(); for (Iterator<Resource> i = resources.iterator(); i.hasNext(); ) { Resource resource = i.next(); String path = resource.getName(); Matcher matcher = pattern.matcher(path); if (matcher.matches()) { String schemaName = matcher.group(1); String schemaVersion = matcher.group(2); if (checkVersion(schemaVersion)) { schemas.add(SchemaImpl.createForContribution( schemaName, schemaVersion, getDescription(), resource, new SourceInfoSupport<ContributionSourceInfo>(this).setSource(resource), // 此方法有一个副作用,将会把当前contribution添加到它所依赖的configuration point的depending contributions列表中。 getContributionSchemaTransformer(getConfigurationPoint().getConfigurationPoints(), this), settings.resourceResolver)); } else { i.remove(); } } else { throw new ConfigurationPointException("Invalid schema name: " + resource); } } if (!resources.isEmpty() && log.isDebugEnabled()) { ToStringBuilder buf = new ToStringBuilder(); buf.format("Found %d versioned schema files for contribution %s:", resources.size(), mainName); buf.append(resources); log.debug(buf.toString()); } return schemas.toArray(new Schema[schemas.size()]); } /** * 检查schemaName-version组合是否为另一个contribution的名字,如果是,则抛弃该组合。 * <p> * 例如:有两个contribution:<code>break</code>和<code>break-if</code>。那么后者的schema: * <code>break-if.xsd</code>就会被误判成前者的某个version版本。该方法用来检查并排除这种情况。 * </p> */ private boolean checkVersion(String schemaVersion) { String name = getName(); for (String v : schemaVersion.split("-")) { name += "-" + v; for (ContributionType type : ContributionType.values()) { if (getConfigurationPoint().getContribution(name, type) != null) { return false; } } } return true; } public String getDescription() { return String.format("Contribution[%s:%s]", getConfigurationPoint().getName(), getName()); } public ConfigurationPointSourceInfo getParent() { return sourceInfo.getParent(); } public Resource getSource() { return sourceInfo.getSource(); } public int getLineNumber() { return sourceInfo.getLineNumber(); } @Override public String toString() { ToStringBuilder buf = new ToStringBuilder(); buf.format("Contribution[toConfigurationPoint=%s, name=%s, type=%s, class=%s]", getConfigurationPoint().getName(), getName(), getType(), implementationClassName).start(); buf.append(getSchemas()); return buf.end().toString(); } }