/*
* Copyright (C) 2013 salesforce.com, inc.
*
* 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 org.auraframework.impl.root.component;
import static org.auraframework.instance.ValueProviderType.LABEL;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.auraframework.Aura;
import org.auraframework.builder.BaseComponentDefBuilder;
import org.auraframework.def.AttributeDef;
import org.auraframework.def.AttributeDefRef;
import org.auraframework.def.BaseComponentDef;
import org.auraframework.def.ClientLibraryDef;
import org.auraframework.def.ComponentDef;
import org.auraframework.def.ControllerDef;
import org.auraframework.def.DefDescriptor;
import org.auraframework.def.DefDescriptor.DefType;
import org.auraframework.def.Definition;
import org.auraframework.def.DependencyDef;
import org.auraframework.def.DesignDef;
import org.auraframework.def.EventHandlerDef;
import org.auraframework.def.HelperDef;
import org.auraframework.def.ImportDef;
import org.auraframework.def.InterfaceDef;
import org.auraframework.def.ModelDef;
import org.auraframework.def.ProviderDef;
import org.auraframework.def.RegisterEventDef;
import org.auraframework.def.RendererDef;
import org.auraframework.def.ResourceDef;
import org.auraframework.def.RootDefinition;
import org.auraframework.def.StyleDef;
import org.auraframework.def.TestSuiteDef;
import org.auraframework.def.ThemeDef;
import org.auraframework.expression.PropertyReference;
import org.auraframework.impl.root.AttributeDefRefImpl;
import org.auraframework.impl.root.RootDefinitionImpl;
import org.auraframework.impl.root.intf.InterfaceDefImpl;
import org.auraframework.impl.system.DefDescriptorImpl;
import org.auraframework.impl.util.AuraUtil;
import org.auraframework.instance.GlobalValueProvider;
import org.auraframework.instance.ValueProviderType;
import org.auraframework.service.DefinitionService;
import org.auraframework.system.AuraContext;
import org.auraframework.system.AuraContext.Mode;
import org.auraframework.system.MasterDefRegistry;
import org.auraframework.throwable.AuraUnhandledException;
import org.auraframework.throwable.quickfix.DefinitionNotFoundException;
import org.auraframework.throwable.quickfix.InvalidDefinitionException;
import org.auraframework.throwable.quickfix.InvalidExpressionException;
import org.auraframework.throwable.quickfix.QuickFixException;
import org.auraframework.util.json.Json;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
public abstract class BaseComponentDefImpl<T extends BaseComponentDef> extends
RootDefinitionImpl<T> implements BaseComponentDef, Serializable {
public static final DefDescriptor<InterfaceDef> ROOT_MARKER = DefDescriptorImpl.getInstance(
"markup://aura:rootComponent", InterfaceDef.class);
private static final long serialVersionUID = -2485193714215681494L;
private final boolean isAbstract;
private final boolean isExtensible;
private final boolean isTemplate;
private final DefDescriptor<ModelDef> modelDefDescriptor;
private final DefDescriptor<T> extendsDescriptor;
private final DefDescriptor<ComponentDef> templateDefDescriptor;
private final DefDescriptor<TestSuiteDef> testSuiteDefDescriptor;
private final DefDescriptor<StyleDef> styleDescriptor;
private final List<DefDescriptor<RendererDef>> rendererDescriptors;
private final List<DefDescriptor<HelperDef>> helperDescriptors;
private final List<DefDescriptor<ResourceDef>> resourceDescriptors;
private final DefDescriptor<ControllerDef> compoundControllerDescriptor;
private final DefDescriptor<ThemeDef> cmpThemeDescriptor;
private final DefDescriptor<DesignDef> designDefDescriptor;
private final Set<DefDescriptor<InterfaceDef>> interfaces;
private final List<DefDescriptor<ControllerDef>> controllerDescriptors;
private final Map<String, RegisterEventDef> events;
private final List<EventHandlerDef> eventHandlers;
private final List<ImportDef> imports;
private final List<AttributeDefRef> facets;
private final Set<PropertyReference> expressionRefs;
private final RenderType render;
private final WhitespaceBehavior whitespaceBehavior;
private final List<DependencyDef> dependencies;
private final List<ClientLibraryDef> clientLibraries;
private final int hashCode;
private transient Boolean localDeps = null;
protected BaseComponentDefImpl(Builder<T> builder) {
super(builder);
this.modelDefDescriptor = builder.modelDefDescriptor;
this.controllerDescriptors = AuraUtil.immutableList(builder.controllerDescriptors);
this.interfaces = AuraUtil.immutableSet(builder.interfaces);
if (builder.extendsDescriptor != null) {
this.extendsDescriptor = builder.extendsDescriptor;
} else {
if (this.interfaces.contains(ROOT_MARKER)) {
this.extendsDescriptor = null;
} else {
this.extendsDescriptor = getDefaultExtendsDescriptor();
}
}
this.templateDefDescriptor = builder.templateDefDescriptor;
this.events = AuraUtil.immutableMap(builder.events);
this.eventHandlers = AuraUtil.immutableList(builder.eventHandlers);
this.imports = AuraUtil.immutableList(builder.imports);
this.styleDescriptor = builder.styleDescriptor;
this.rendererDescriptors = builder.rendererDescriptors;
this.helperDescriptors = builder.helperDescriptors;
this.resourceDescriptors = builder.resourceDescriptors;
this.isAbstract = builder.isAbstract;
this.isExtensible = builder.isExtensible;
this.isTemplate = builder.isTemplate;
this.testSuiteDefDescriptor = builder.testSuiteDefDescriptor;
this.facets = AuraUtil.immutableList(builder.facets);
this.dependencies = AuraUtil.immutableList(builder.dependencies);
this.clientLibraries = AuraUtil.immutableList(builder.clientLibraries);
this.render = builder.renderType;
this.whitespaceBehavior = builder.whitespaceBehavior;
this.cmpThemeDescriptor = builder.cmpThemeDescriptor;
this.designDefDescriptor = builder.designDefDescriptor;
this.expressionRefs = AuraUtil.immutableSet(builder.expressionRefs);
if (getDescriptor() != null) {
this.compoundControllerDescriptor = DefDescriptorImpl.getAssociateDescriptor(getDescriptor(),
ControllerDef.class, DefDescriptor.COMPOUND_PREFIX);
} else {
this.compoundControllerDescriptor = null;
}
this.hashCode = AuraUtil.hashCode(super.hashCode(), events, controllerDescriptors, modelDefDescriptor,
extendsDescriptor, interfaces, rendererDescriptors, helperDescriptors, resourceDescriptors,
cmpThemeDescriptor, imports);
}
/**
* @throws QuickFixException
* @see Definition#validateDefinition()
*/
@Override
public void validateDefinition() throws QuickFixException {
super.validateDefinition();
for (DependencyDef def : dependencies) {
def.validateDefinition();
}
for (AttributeDef att : this.attributeDefs.values()) {
att.validateDefinition();
if (events.containsKey(att.getName())) {
throw new InvalidDefinitionException(String.format(
"Cannot define an attribute and register an event with the same name: %s", att.getName()),
getLocation());
}
}
for (AttributeDefRef facet : this.facets) {
facet.validateDefinition();
}
for (RegisterEventDef def : events.values()) {
def.validateDefinition();
}
for (EventHandlerDef def : eventHandlers) {
def.validateDefinition();
}
for (ImportDef def : imports) {
def.validateDefinition();
}
// an abstract component that you can't extend is pretty useless
if (this.isAbstract() && !this.isExtensible()) {
throw new InvalidDefinitionException(String.format(
"Abstract component %s must be extensible.", getDescriptor()), getLocation());
}
if (this.interfaces.contains(ROOT_MARKER)) {
// only aura has root access (this could be solved with namespace
// only visiblity of the rootComponent interface someday)
if (!"aura".equals(this.descriptor.getNamespace())) {
throw new InvalidDefinitionException(
String.format(
"Component %s cannot implement the rootComponent interface because it is not in the aura namespace",
getDescriptor()), getLocation());
}
// cannot be a root and extend something
if (this.extendsDescriptor != null) {
throw new InvalidDefinitionException(
String.format(
"Component %s cannot be a rootComponent and extend %s", getDescriptor(),
this.extendsDescriptor),
getLocation());
}
}
// validate all client libraries
for (ClientLibraryDef def : this.clientLibraries) {
def.validateDefinition();
}
}
@Override
public boolean hasLocalDependencies() throws QuickFixException {
if (localDeps == null) {
computeLocalDependencies();
}
return localDeps == Boolean.TRUE;
}
/**
* Computes the local (server) dependencies.
*
* Terminology: "remote" - a JavaScript provider or renderer "local" - a Java/Apex/server provider, renderer, or
* model
*/
private synchronized void computeLocalDependencies() throws QuickFixException {
if (localDeps != null) {
return;
}
if (modelDefDescriptor != null) {
localDeps = Boolean.TRUE;
return;
}
if (rendererDescriptors != null && !rendererDescriptors.isEmpty()) {
boolean hasRemote = false;
for (DefDescriptor<RendererDef> rendererDescriptor : rendererDescriptors) {
if (!rendererDescriptor.getDef().isLocal()) {
hasRemote = true;
break;
}
}
if (!hasRemote) {
localDeps = Boolean.TRUE;
return;
}
}
if (providerDescriptors != null) {
boolean hasRemote = providerDescriptors.isEmpty();
for (DefDescriptor<ProviderDef> providerDescriptor : providerDescriptors) {
if (!providerDescriptor.getDef().isLocal()) {
hasRemote = true;
break;
}
}
if (!hasRemote) {
localDeps = Boolean.TRUE;
return;
}
}
// Walk the super component tree applying slightly different dependency rules.
T superDef = getSuperDef();
if (superDef != null && superDef.hasLocalDependencies() &&
// super has model
(superDef.getModelDef() != null ||
// or has renderer that's local
(superDef.getRendererDescriptor() != null && superDef.getRendererDescriptor().getDef().isLocal()))) {
// Only local/server models and renderers on the super/parent are considered local dependencies for the
// child.
localDeps = Boolean.TRUE;
return;
}
if (localDeps == null) {
localDeps = Boolean.FALSE;
}
}
@SuppressWarnings("unchecked")
@Override
public void validateReferences() throws QuickFixException {
super.validateReferences();
for (DependencyDef def : dependencies) {
def.validateReferences();
}
for (AttributeDef att : this.attributeDefs.values()) {
att.validateReferences();
}
for (AttributeDefRef facet : this.facets) {
facet.validateReferences();
}
// TODO: lots more validation an stuff!!!!!!! #W-689596
MasterDefRegistry registry = Aura.getDefinitionService().getDefRegistry();
if (extendsDescriptor != null) {
T parentDef = extendsDescriptor.getDef();
if (parentDef == null) {
throw new DefinitionNotFoundException(extendsDescriptor, getLocation());
}
if (parentDef.getDescriptor().equals(descriptor)) {
throw new InvalidDefinitionException(String.format(
"%s cannot extend itself", getDescriptor()), getLocation());
}
if (!parentDef.isExtensible()) {
throw new InvalidDefinitionException(String.format(
"%s cannot extend non-extensible component %s", getDescriptor(), extendsDescriptor),
getLocation());
}
registry.assertAccess(descriptor, parentDef);
SupportLevel support = getSupport();
DefDescriptor<T> extDesc = extendsDescriptor;
while (extDesc != null) {
T extDef = extDesc.getDef();
if (support.ordinal() > extDef.getSupport().ordinal()) {
throw new InvalidDefinitionException(
String.format("%s cannot widen the support level to %s from %s's level of %s",
getDescriptor(),
support, extDesc, extDef.getSupport()), getLocation());
}
extDesc = (DefDescriptor<T>) extDef.getExtendsDescriptor();
}
}
for (DefDescriptor<InterfaceDef> intf : interfaces) {
InterfaceDef interfaze = intf.getDef();
if (interfaze == null) {
throw new DefinitionNotFoundException(intf, getLocation());
}
registry.assertAccess(descriptor, interfaze);
}
for (RegisterEventDef def : events.values()) {
def.validateReferences();
}
for (EventHandlerDef def : eventHandlers) {
def.validateReferences();
}
for (ImportDef def : imports) {
def.validateReferences();
}
// have to do all sorts of craaaazy checks here for dupes and matches
// and bah
validateExpressionRefs();
for (ClientLibraryDef def : this.clientLibraries) {
def.validateReferences();
registry.assertAccess(descriptor, def);
}
}
/**
* Does all the validation of the expressions defined in this component
*/
private void validateExpressionRefs() throws QuickFixException {
for (PropertyReference e : expressionRefs) {
String root = e.getRoot();
ValueProviderType vpt = ValueProviderType.getTypeByPrefix(root);
if (vpt == null) {
// validate that its a foreachs
} else if (vpt.isGlobal()) {
AuraContext lc = Aura.getContextService().getCurrentContext();
GlobalValueProvider gvp = lc.getGlobalProviders().get(vpt);
if (gvp != null) {
PropertyReference stem = e.getStem();
if (stem == null) {
throw new InvalidExpressionException("Expression didn't have enough terms: " + e, e.getLocation());
}
gvp.validate(stem);
}
} else if (vpt == ValueProviderType.VIEW) {
if (e.getStem() != null) { // checks for private attributes used in expressions ..
String stem = e.getStem().toString();
AttributeDef attr = getAttributeDef(stem);
if ((attr != null) && (attr.getVisibility() == Visibility.PRIVATE)
&& (!this.attributeDefs.values().contains(attr))) {
throw new InvalidDefinitionException(String.format(
"Expression %s refers to a private attribute '%s' ", e, attr), e.getLocation());
}
}
}
}
}
/**
* Retrieve labels for a list of descriptors.
*/
private <D extends Definition> void retrieveListLabels(DefinitionService definitionService,
List<DefDescriptor<D>> descriptors) throws QuickFixException {
if (descriptors != null) {
for (DefDescriptor<D> desc : descriptors) {
definitionService.getDefinition(desc).retrieveLabels();
}
}
}
@Override
public void retrieveLabels() throws QuickFixException {
GlobalValueProvider labelProvider = Aura.getContextService().getCurrentContext().getGlobalProviders()
.get(LABEL);
for (PropertyReference e : expressionRefs) {
if (e.getRoot().equals(LABEL.getPrefix())) {
labelProvider.getValue(e.getStem());
}
}
DefinitionService definitionService = Aura.getDefinitionService();
retrieveListLabels(definitionService, controllerDescriptors);
retrieveListLabels(definitionService, rendererDescriptors);
retrieveListLabels(definitionService, helperDescriptors);
retrieveListLabels(definitionService, providerDescriptors);
}
@Override
public List<DependencyDef> getDependencies() {
return this.dependencies;
}
/**
* Recursively adds the ComponentDescriptors of all components in this ComponentDef's children to the provided set.
* The set may then be used to analyze freshness of all of those types to see if any of them should be recompiled
* from source.
*
* @param dependencies A Set that this method will append RootDescriptors to for every RootDef that this
* ComponentDef imports
* @throws QuickFixException
*/
@Override
public void appendDependencies(Set<DefDescriptor<?>> dependencies) {
super.appendDependencies(dependencies);
for (AttributeDefRef facet : this.facets) {
facet.appendDependencies(dependencies);
}
if (extendsDescriptor != null) {
dependencies.add(extendsDescriptor);
}
for (DefDescriptor<InterfaceDef> intf : interfaces) {
dependencies.add(intf);
}
for (RegisterEventDef register : events.values()) {
register.appendDependencies(dependencies);
}
for (EventHandlerDef handler : eventHandlers) {
handler.appendDependencies(dependencies);
}
if (controllerDescriptors != null) {
dependencies.addAll(controllerDescriptors);
}
if (modelDefDescriptor != null) {
dependencies.add(modelDefDescriptor);
}
if (rendererDescriptors != null) {
dependencies.addAll(rendererDescriptors);
}
if (helperDescriptors != null) {
dependencies.addAll(helperDescriptors);
}
if (resourceDescriptors != null) {
dependencies.addAll(resourceDescriptors);
}
if (styleDescriptor != null) {
dependencies.add(styleDescriptor);
}
if (templateDefDescriptor != null) {
dependencies.add(templateDefDescriptor);
}
if (cmpThemeDescriptor != null) {
dependencies.add(cmpThemeDescriptor);
}
if (designDefDescriptor != null) {
dependencies.add(designDefDescriptor);
}
if (imports != null) {
for (ImportDef imported : imports) {
dependencies.add(imported.getDescriptor());
}
}
for (DependencyDef dep : this.dependencies) {
dep.appendDependencies(dependencies);
}
}
@Override
public void appendSupers(Set<DefDescriptor<?>> supers) throws QuickFixException {
if (getExtendsDescriptor() != null) {
supers.add(getExtendsDescriptor());
}
for (DefDescriptor<InterfaceDef> interfaze : getInterfaces()) {
supers.add(interfaze);
}
}
@Override
public void addClientLibs(List<ClientLibraryDef> clientLibs) {
clientLibs.addAll(this.clientLibraries);
}
@Override
public Set<ResourceDef> getResourceDefs() throws QuickFixException {
Set<ResourceDef> resourceDefs = Sets.newHashSet();
for (DefDescriptor<ResourceDef> resourceDesc : this.resourceDescriptors) {
if (resourceDesc.getDef() != null) {
resourceDefs.add(resourceDesc.getDef());
}
}
return resourceDefs;
}
/**
* This is used to validate by the compiler to validate EventDefRefs.
*
* @return all the events this component can fire, including those inherited
* @throws QuickFixException
*/
@Override
public Map<String, RegisterEventDef> getRegisterEventDefs() throws QuickFixException {
Map<String, RegisterEventDef> ret = new LinkedHashMap<>();
if (extendsDescriptor != null) {
ret.putAll(getSuperDef().getRegisterEventDefs());
}
for (DefDescriptor<InterfaceDef> intf : interfaces) {
InterfaceDef intfDef = intf.getDef();
ret.putAll(intfDef.getRegisterEventDefs());
}
if (ret.isEmpty()) {
return events;
} else {
ret.putAll(events);
return Collections.unmodifiableMap(ret);
}
}
/**
* @return all the handlers on this component, including those inherited
* @throws QuickFixException
*/
@Override
public Collection<EventHandlerDef> getHandlerDefs() throws QuickFixException {
return eventHandlers;
}
/**
* @return all the library imports from this component, including those inherited
* @throws QuickFixException
*/
@Override
public Collection<ImportDef> getImportDefs() throws QuickFixException {
return imports;
}
/**
* @return all the attributes for this component, including those inherited from a super component
* @throws QuickFixException
*/
@Override
public Map<DefDescriptor<AttributeDef>, AttributeDef> getAttributeDefs() throws QuickFixException {
Map<DefDescriptor<AttributeDef>, AttributeDef> map = new LinkedHashMap<>();
if (extendsDescriptor != null) {
map.putAll(getSuperDef().getAttributeDefs());
}
for (DefDescriptor<InterfaceDef> intf : interfaces) {
InterfaceDef intfDef = intf.getDef();
for (Map.Entry<DefDescriptor<AttributeDef>, AttributeDef> entry : intfDef.getAttributeDefs().entrySet()) {
DefDescriptor<AttributeDef> desc = entry.getKey();
if (map.containsKey(desc)) {
// FIXMEDLP - do some validation #W-690040
}
map.put(desc, entry.getValue());
}
}
if (map.isEmpty()) {
return attributeDefs;
} else {
map.putAll(attributeDefs);
return Collections.unmodifiableMap(map);
}
}
@Override
public List<DefDescriptor<ControllerDef>> getControllerDefDescriptors() throws QuickFixException {
List<DefDescriptor<ControllerDef>> ret;
if (extendsDescriptor != null) {
ret = new ArrayList<>();
ret.addAll(this.controllerDescriptors);
ret.addAll(getSuperDef().getControllerDefDescriptors());
} else {
ret = this.controllerDescriptors;
}
return ret;
}
@Override
public ControllerDef getControllerDef() throws QuickFixException {
if (this.controllerDescriptors.isEmpty()) {
if (this.extendsDescriptor != null) {
return getSuperDef().getControllerDef();
} else {
return null;
}
} else {
return compoundControllerDescriptor.getDef();
}
}
@Override
public ControllerDef getDeclaredControllerDef() throws QuickFixException {
return !this.controllerDescriptors.isEmpty() && compoundControllerDescriptor != null ? compoundControllerDescriptor.getDef() : null;
}
@Override
public StyleDef getStyleDef() throws QuickFixException {
return styleDescriptor == null ? null : styleDescriptor.getDef();
}
@Override
public DefDescriptor<T> getExtendsDescriptor() {
return extendsDescriptor;
}
@Override
public DefDescriptor<DesignDef> getDesignDefDescriptor() {
return designDefDescriptor;
}
@Override
public DefDescriptor<ComponentDef> getTemplateDefDescriptor() {
return templateDefDescriptor;
}
@Override
public ComponentDef getTemplateDef() throws QuickFixException {
if (templateDefDescriptor == null) {
return getSuperDef().getTemplateDef();
} else {
return templateDefDescriptor.getDef();
}
}
@Override
public DefDescriptor<StyleDef> getStyleDescriptor() {
return styleDescriptor;
}
@Override
public DefDescriptor<ThemeDef> getCmpTheme() {
return cmpThemeDescriptor;
}
@Override
public boolean isAbstract() {
return isAbstract;
}
@Override
public boolean isExtensible() {
return isExtensible;
}
@Override
public boolean isTemplate() {
return isTemplate;
}
@Override
public Set<DefDescriptor<InterfaceDef>> getInterfaces() {
return interfaces;
}
private Set<DefDescriptor<InterfaceDef>> getAllInterfaces() throws QuickFixException {
Set<DefDescriptor<InterfaceDef>> ret = Sets.newLinkedHashSet();
for (DefDescriptor<InterfaceDef> intf : interfaces) {
addAllInterfaces(intf, ret);
}
return ret;
}
private void addAllInterfaces(DefDescriptor<InterfaceDef> intf, Set<DefDescriptor<InterfaceDef>> set)
throws QuickFixException {
set.add(intf);
for (DefDescriptor<InterfaceDef> zuper : intf.getDef().getExtendsDescriptors()) {
set.add(zuper);
addAllInterfaces(zuper, set);
}
}
protected T getSuperDef() throws QuickFixException {
T ret = null;
if (extendsDescriptor != null) {
ret = extendsDescriptor.getDef();
}
return ret;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof BaseComponentDefImpl) {
BaseComponentDefImpl<?> other = (BaseComponentDefImpl<?>) obj;
return getDescriptor().equals(other.getDescriptor())
&& controllerDescriptors.equals(other.controllerDescriptors)
&& (modelDefDescriptor == null ? other.modelDefDescriptor == null
: modelDefDescriptor.equals(other.modelDefDescriptor))
&& (extendsDescriptor == null ? other.extendsDescriptor == null
: extendsDescriptor.equals(other.extendsDescriptor))
&& (cmpThemeDescriptor == null ? other.cmpThemeDescriptor == null
: cmpThemeDescriptor.equals(other.cmpThemeDescriptor))
&& events.equals(other.events)
&& getLocation().equals(other.getLocation());
}
return false;
}
/**
* @see RootDefinitionImpl#hashCode()
*/
@Override
public int hashCode() {
return hashCode;
}
/**
* Serialize this component to json. The output will include all of the attributes, events, and handlers inherited.
* It doesn't yet include inherited ComponentDefRefs, but maybe it should.
*/
@Override
public void serialize(Json json) throws IOException {
try {
AuraContext context = Aura.getContextService().getCurrentContext();
Mode mode = context.getMode();
boolean preloaded = context.isPreloaded(getDescriptor());
boolean preloading = context.isPreloading();
if (preloaded) {
json.writeValue(descriptor);
} else {
json.writeMapBegin();
json.writeMapEntry("descriptor", descriptor);
context.pushCallingDescriptor(descriptor);
try {
RendererDef rendererDef = getRendererDef();
if (rendererDef != null && !rendererDef.isLocal()) {
json.writeMapEntry("rendererDef", rendererDef);
}
} finally {
context.popCallingDescriptor();
}
HelperDef helperDef = getHelperDef();
if (helperDef != null && !helperDef.isLocal()) {
json.writeMapEntry("helperDef", helperDef);
}
json.writeMapEntry("styleDef", getStyleDef());
json.writeMapEntry("controllerDef", getControllerDef());
json.writeMapEntry("modelDef", getModelDef());
json.writeMapEntry("superDef", getSuperDef());
if (preloading) {
json.writeMapEntry("isCSSPreloaded", preloading);
}
Collection<AttributeDef> attrDefs = getAttributeDefs().values();
if (!attrDefs.isEmpty()) {
json.writeMapEntry("attributeDefs", attrDefs);
}
Set<DefDescriptor<InterfaceDef>> allInterfaces = getAllInterfaces();
if (allInterfaces != null && !allInterfaces.isEmpty()) {
json.writeMapEntry("interfaces", allInterfaces);
}
Collection<RegisterEventDef> regevents = getRegisterEventDefs().values();
if (!regevents.isEmpty()) {
json.writeMapEntry("registerEventDefs", regevents);
}
Collection<EventHandlerDef> handlers = getHandlerDefs();
if (!handlers.isEmpty()) {
json.writeMapEntry("handlerDefs", handlers);
}
Collection<ImportDef> imports = getImportDefs();
if (!imports.isEmpty()) {
json.writeMapEntry("imports", imports);
}
if (!facets.isEmpty()) {
json.writeMapEntry("facets", facets);
}
boolean local = hasLocalDependencies();
if (local) {
json.writeMapEntry("hasServerDeps", true);
}
if (isAbstract) {
json.writeMapEntry("isAbstract", isAbstract);
}
ProviderDef providerDef = getProviderDef();
if (providerDef != null && !providerDef.isLocal()) {
json.writeMapEntry("providerDef", providerDef);
}
if (subDefs != null) {
json.writeMapEntry("subDefs", subDefs.values());
}
if (mode.equals(Mode.AUTOJSTEST)) {
json.writeMapEntry("testSuiteDef", getTestSuiteDef());
}
serializeFields(json);
json.writeMapEnd();
}
} catch (QuickFixException e) {
throw new AuraUnhandledException("unhandled exception", e);
}
}
protected abstract void serializeFields(Json json) throws IOException,
QuickFixException;
/**
* @see ComponentDef#getRendererDescriptor()
*/
@Override
public DefDescriptor<RendererDef> getRendererDescriptor() throws QuickFixException {
if (rendererDescriptors != null && rendererDescriptors.size() == 1) {
return rendererDescriptors.get(0);
}
RendererDef rendererDef = getRendererDef();
if (rendererDef != null) {
return rendererDef.getDescriptor();
}
return null;
}
@Override
public RendererDef getLocalRendererDef() throws QuickFixException {
RendererDef def = null;
if (rendererDescriptors != null) {
for (DefDescriptor<RendererDef> desc : rendererDescriptors) {
def = desc.getDef();
if (def.isLocal()) {
break;
} else {
def = null;
}
}
}
return def;
}
/**
* @return The primary renderer def. If multiple exist, this will be the remote one.
* @throws QuickFixException
*/
public RendererDef getRendererDef() throws QuickFixException {
RendererDef def = null;
if (rendererDescriptors != null) {
for (DefDescriptor<RendererDef> desc : rendererDescriptors) {
def = desc.getDef();
if (!def.isLocal()) {
break;
}
}
}
return def;
}
/**
* @return The primary helper def. If multiple exist, this will be the remote one.
* @throws QuickFixException
*/
@Override
public HelperDef getHelperDef() throws QuickFixException {
HelperDef def = null;
if (helperDescriptors != null) {
for (DefDescriptor<HelperDef> desc : helperDescriptors) {
def = desc.getDef();
if (!def.isLocal()) {
break;
}
}
}
return def;
}
public TestSuiteDef getTestSuiteDef() throws QuickFixException {
return testSuiteDefDescriptor == null ? null : testSuiteDefDescriptor.getDef();
}
/**
* @see ComponentDef#getLocalModelDefDescriptor()
*/
@Override
public DefDescriptor<ModelDef> getLocalModelDefDescriptor() {
return modelDefDescriptor;
}
/**
* @see ComponentDef#getModelDef()
*/
@Override
public ModelDef getModelDef() throws QuickFixException {
AuraContext context = Aura.getContextService().getCurrentContext();
context.pushCallingDescriptor(descriptor);
try {
return modelDefDescriptor == null ? null : modelDefDescriptor.getDef();
} finally {
context.popCallingDescriptor();
}
}
/**
* @see ComponentDef#getModelDefDescriptors()
*/
@Override
public List<DefDescriptor<ModelDef>> getModelDefDescriptors() throws QuickFixException {
List<DefDescriptor<ModelDef>> ret = new ArrayList<>();
if (modelDefDescriptor != null) {
ret.add(modelDefDescriptor);
}
if (extendsDescriptor != null) {
ret.addAll(getSuperDef().getModelDefDescriptors());
}
return ret;
}
@Override
public List<ClientLibraryDef> getClientLibraries() {
return clientLibraries;
}
public static abstract class Builder<T extends BaseComponentDef> extends
RootDefinitionImpl.Builder<T> implements BaseComponentDefBuilder<T> {
public Builder(Class<T> defClass) {
super(defClass);
}
public boolean isAbstract;
public boolean isExtensible;
public boolean isTemplate;
public DefDescriptor<ModelDef> modelDefDescriptor;
public DefDescriptor<T> extendsDescriptor;
public DefDescriptor<ComponentDef> templateDefDescriptor;
public DefDescriptor<TestSuiteDef> testSuiteDefDescriptor;
public DefDescriptor<StyleDef> styleDescriptor;
public DefDescriptor<ThemeDef> cmpThemeDescriptor;
public DefDescriptor<DesignDef> designDefDescriptor;
public List<DefDescriptor<RendererDef>> rendererDescriptors;
public List<DefDescriptor<HelperDef>> helperDescriptors;
public List<DefDescriptor<ResourceDef>> resourceDescriptors;
public List<AttributeDefRef> facets;
public Set<DefDescriptor<InterfaceDef>> interfaces;
public List<DefDescriptor<ControllerDef>> controllerDescriptors;
public Map<String, RegisterEventDef> events;
public List<EventHandlerDef> eventHandlers;
public List<ImportDef> imports;
public Set<PropertyReference> expressionRefs;
public String render;
public WhitespaceBehavior whitespaceBehavior;
List<DependencyDef> dependencies;
public List<ClientLibraryDef> clientLibraries;
private RenderType renderType;
@Override
public Builder<T> setFacet(String key, Object value) {
if (facets == null) {
facets = Lists.newArrayList();
}
AttributeDefRefImpl.Builder atBuilder = new AttributeDefRefImpl.Builder();
atBuilder.setDescriptor(key);
atBuilder.setLocation(getLocation());
atBuilder.setValue(value);
facets.add(atBuilder.build());
return this;
}
public void addRenderer(String name) {
if (this.rendererDescriptors == null) {
this.rendererDescriptors = Lists.newArrayList();
}
this.rendererDescriptors.add(DefDescriptorImpl.getInstance(name, RendererDef.class));
}
public void addHelper(String name) {
if (this.helperDescriptors == null) {
this.helperDescriptors = Lists.newArrayList();
}
this.helperDescriptors.add(DefDescriptorImpl.getInstance(name, HelperDef.class));
}
public void addResource(String name) {
if (this.resourceDescriptors == null) {
this.resourceDescriptors = Lists.newArrayList();
}
this.resourceDescriptors.add(DefDescriptorImpl.getInstance(name, ResourceDef.class));
}
@Override
public Builder<T> addControllerDef(ControllerDef controllerDef) {
if (controllerDescriptors == null) {
controllerDescriptors = Lists.newArrayList();
}
controllerDescriptors.add(controllerDef.getDescriptor());
return this;
}
@Override
public Builder<T> addInterfaceDef(InterfaceDef interfaceDef) {
if (interfaces == null) {
interfaces = Sets.newLinkedHashSet();
}
interfaces.add(interfaceDef.getDescriptor());
return this;
}
@Override
public Builder<T> addRendererDef(RendererDef rendererDef) {
if (rendererDescriptors == null) {
rendererDescriptors = Lists.newArrayList();
}
rendererDescriptors.add(rendererDef.getDescriptor());
return this;
}
@Override
public Builder<T> setAbstract(boolean abs) {
this.isAbstract = abs;
return this;
}
@Override
public Builder<T> setExtensible(boolean extensible) {
this.isExtensible = extensible;
return this;
}
@Override
public Builder<T> setModelDef(ModelDef modelDef) {
this.modelDefDescriptor = modelDef.getDescriptor();
return this;
}
@Override
public Builder<T> setRenderType(RenderType renderType) {
this.render = renderType.name();
return this;
}
@Override
public Builder<T> setWhitespaceBehavior(WhitespaceBehavior whitespaceBehavior) {
this.whitespaceBehavior = whitespaceBehavior;
return this;
}
@Override
public Builder<T> setTemplateDef(ComponentDef templateDef) {
this.templateDefDescriptor = templateDef.getDescriptor();
return this;
}
@Override
public Builder<T> setDesignDef(DesignDef designDef) {
this.designDefDescriptor = designDef.getDescriptor();
return this;
}
@Override
public Builder<T> setTemplate(String templateName) {
this.templateDefDescriptor = Aura.getDefinitionService().getDefDescriptor(templateName, ComponentDef.class);
return this;
}
public Builder<T> addDependency(DependencyDef dependency) {
if (this.dependencies == null) {
this.dependencies = Lists.newArrayList();
}
this.dependencies.add(dependency);
return this;
}
@Override
public Builder<T> setStyleDef(StyleDef styleDef) {
this.styleDescriptor = styleDef.getDescriptor();
return this;
}
@Override
public Builder<T> addClientLibrary(ClientLibraryDef clientLibrary) {
if (this.clientLibraries == null) {
this.clientLibraries = Lists.newArrayList();
}
this.clientLibraries.add(clientLibrary);
return this;
}
protected void finish() {
if (render == null) {
this.renderType = RenderType.AUTO;
} else {
try {
this.renderType = RenderType.valueOf(render.toUpperCase());
} catch (Exception e) {
setParseError(e);
}
}
}
}
/**
* @see RootDefinition#isInstanceOf(DefDescriptor)
*/
@Override
public boolean isInstanceOf(DefDescriptor<? extends RootDefinition> other) throws QuickFixException {
switch (other.getDefType()) {
case INTERFACE:
for (DefDescriptor<InterfaceDef> intf : interfaces) {
if (intf.equals(other) || intf.getDef().isInstanceOf(other)) {
return true;
}
}
return (extendsDescriptor != null && getSuperDef().isInstanceOf(other));
case COMPONENT:
case APPLICATION:
return descriptor.equals(other)
|| (extendsDescriptor != null && getSuperDef()
.isInstanceOf(other));
default:
return false;
}
}
/**
* @see ComponentDef#getFacets()
*/
@Override
public List<AttributeDefRef> getFacets() {
return facets;
}
@Override
public List<DefDescriptor<?>> getBundle() {
List<DefDescriptor<?>> ret = Lists.newArrayList();
if (controllerDescriptors != null) {
ret.addAll(controllerDescriptors);
}
if (rendererDescriptors != null) {
ret.addAll(rendererDescriptors);
}
if (modelDefDescriptor != null) {
ret.add(modelDefDescriptor);
}
if (providerDescriptors != null) {
ret.addAll(providerDescriptors);
}
if (styleDescriptor != null) {
ret.add(styleDescriptor);
}
if (helperDescriptors != null) {
ret.addAll(helperDescriptors);
}
if (documentationDescriptor != null) {
ret.add(documentationDescriptor);
}
return ret;
}
/**
* This should not be here it should be a call off of MDR.
*/
@Override
public boolean isLocallyRenderable() throws QuickFixException {
return isLocallyRenderable(Sets.<DefDescriptor<?>> newLinkedHashSet());
}
/**
* Helper routine for public call. DIE! please?
*
* @param already the set of processed descriptors.
*/
private boolean isLocallyRenderable(Set<DefDescriptor<?>> already) throws QuickFixException {
if (render == RenderType.CLIENT) {
return false;
} else if (render == RenderType.SERVER) {
return true;
}
//
// FIXME: OMG W-1501702 really?!?!?!
//
// We desperately need to make this go away. It is heinousness
// incarnate, but the entirety of server side rendering is blocking
// this.
//
// Currently, the server side throws an UnsupportedOperationException,
// so the styles (which is one part that currently breaks) never get
// rendered.
//
// also see W-922563
//
// This will probably stay here til we fix server side rendering (or at
// least the style part). Also, we need to allow dual renderers.
//
if (this.getDescriptor().getQualifiedName().equals("markup://aura:placeholder")) {
return true;
}
RendererDef rendererDef = getLocalRendererDef();
boolean ret = false;
// have a local renderer?
if (rendererDef == null) {
// no?
rendererDef = getRendererDef();
// ok, is there a remote one?
ret = rendererDef == null;
} else {
// cool.
ret = true;
}
// If we've gotten this far, let's check for remote providers
if (ret) {
ret = ret && isInConcreteAndHasLocalProvider();
}
// If we've gotten this far, let's check for controllers.
if (ret) {
ret = ret && getControllerDefDescriptors().isEmpty();
}
// If we've gotten this far, let's check for Styles (server rendering
// doesn't work with styles) W-922563
if (ret) {
ret = ret && getStyleDescriptor() == null;
}
// If we've gotten this far, let's spider dependencies.
if (ret) {
Set<DefDescriptor<?>> deps = Sets.newLinkedHashSet();
appendDependencies(deps);
for (DefDescriptor<?> dep : deps) {
if (!already.contains(dep)) {
already.add(dep);
if (dep.getDefType() == DefType.COMPONENT || dep.getDefType() == DefType.APPLICATION) {
BaseComponentDefImpl<?> depDef = (BaseComponentDefImpl<?>) dep.getDef();
if (depDef != this) {
ret = ret && depDef.isLocallyRenderable(already);
if (!ret) {
return false;
}
}
} else if (dep.getDefType() == DefType.INTERFACE) {
InterfaceDefImpl depDef = (InterfaceDefImpl) dep.getDef();
ret = ret && depDef.isInConcreteAndHasLocalProvider();
} else if (dep.getDefType() == DefType.LAYOUTS) {
return false;
}
}
}
}
return ret;
}
@Override
public RenderType getRender() {
return render;
}
@Override
public WhitespaceBehavior getWhitespaceBehavior() {
return whitespaceBehavior;
}
@Override
public abstract DefDescriptor<T> getDefaultExtendsDescriptor();
}