Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
Copyright (c) 2022 Kevin Jones, All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
*/
package com.nawforce.apexlink.org
import com.nawforce.apexlink.names.TypeNames
import com.nawforce.apexlink.names.TypeNames.TypeNameUtils
import com.nawforce.apexlink.types.apex.{FullDeclaration, TriggerDeclaration}
import com.nawforce.apexlink.types.core.{DependentType, TypeId}
import com.nawforce.apexlink.types.other.{
ComponentDeclaration,
InterviewDeclaration,
LabelDeclaration,
PageDeclaration
}
import com.nawforce.apexlink.types.platform.PlatformTypes
import com.nawforce.pkgforce.diagnostics.LoggerOps
import com.nawforce.pkgforce.documents._
import com.nawforce.pkgforce.names.{EncodedName, Name, TypeName}
import com.nawforce.pkgforce.path.PathLike
import com.nawforce.pkgforce.stream._
import com.nawforce.runtime.parsers.SourceData
import scala.collection.immutable.ArraySeq
trait ModuleRefresh {
self: OPM.Module =>
def refreshInternal(existingLabels: LabelDeclaration): Seq[(TypeId, Set[TypeId])] = {
val newLabels = createLabelDeclaration()
val holders = existingLabels.getTypeDependencyHolders
newLabels.setTypeDependencyHolders(holders)
replaceType(newLabels.typeName, Some(newLabels))
newLabels.validate()
Seq((newLabels.typeId, holders.toSet))
}
/* Replace a path, returns the TypeId of the type that was updated and a Set of TypeIds for the dependency
* holders of that type. */
def refreshInternal(path: PathLike): Seq[(TypeId, Set[TypeId])] = {
PlatformTypes.withLoadingObserver(schemaSObjectType) {
checkPathInPackageOrThrow(path)
val doc = MetadataDocument(path).getOrElse(
throw new IllegalArgumentException(s"Metadata type is not supported for '$path'")
)
// Update internal document tracking
val sourceOpt = resolveSource(path)
if (sourceOpt.isEmpty) {
pkg.org.issueManager.pop(doc.path)
if (!index.remove(doc)) {
LoggerOps.debug(s"Refresh of deleted ${doc.path} was not in use, ignoring.")
return Seq()
}
} else if (!index.upsert(doc)) {
LoggerOps.debug(s"Refresh of non-index file ${doc.path}.")
return Seq()
}
// Create type & forward holders to limit need for invalidation chaining
createTypes(doc, sourceOpt) match {
case None => Seq() // doc can't create a type (i.e. meta files)
case Some(newTypes) =>
val typeId = TypeId(this, doc.typeName(namespace))
if (newTypes.nonEmpty) {
newTypes.map(newType => {
val existingType = getDependentType(newType.typeName)
val holders = existingType
.map(_.getTypeDependencyHolders)
.getOrElse(DependentType.emptyTypeDependencyHolders)
newType.setTypeDependencyHolders(holders)
// Update and validate
replaceType(newType.typeName, Some(newType))
newType.validate()
(typeId, holders.toSet)
})
} else {
val existingType = getDependentType(typeId.typeName)
val holders = existingType
.map(_.getTypeDependencyHolders)
.getOrElse(DependentType.emptyTypeDependencyHolders)
removeTypes(doc)
Seq((typeId, holders.toSet))
}
}
}
}
private def getDependentType(typeName: TypeName): Option[DependentType] = {
types
.get(typeName)
.flatMap {
case dt: DependentType => Some(dt)
case _ => None
}
}
private def removeTypes(doc: MetadataDocument): Unit = {
doc match {
case doc: SObjectDocument =>
if (doc.path.toString.endsWith("object-meta.xml"))
removeSObjectTypes(doc.path.parent.basename)
else
removeSObjectTypes(doc.path.basename.replaceFirst("\\.object$", ""))
case _: SObjectFieldDocument | _: SObjectFieldSetDocument | _: SObjectSharingReasonDocument =>
val sObjectDir = doc.path.parent.parent
removeSObjectTypes(sObjectDir.basename)
case _ =>
types.remove(doc.typeName(namespace)).foreach(_.dead = true)
}
}
private def removeSObjectTypes(sobjectName: String): Unit = {
val name = EncodedName(sobjectName)
if (name.ext.contains(Name("c"))) {
val typeName = TypeName(name.fullName, Nil, Some(TypeNames.Schema))
val objectNames = Seq(
typeName,
typeName.withNameReplace("__c$", "__Share"),
typeName.withNameReplace("__c$", "__Feed"),
typeName.withNameReplace("__c$", "__History")
)
objectNames.foreach(typeName => schemaSObjectType.remove(typeName.name))
objectNames.foreach(typeName => types.remove(typeName).foreach(_.dead = true))
}
}
private def createLabelDeclaration(): LabelDeclaration = {
val events = LabelGenerator.iterator(index)
val stream = new PackageStream(ArraySeq.unsafeWrapArray(events.toArray))
LabelDeclaration(this).merge(stream)
}
private def createTypes(
doc: MetadataDocument,
source: Option[SourceData]
): Option[Seq[DependentType]] = {
doc match {
case _: ApexTriggerMetaDocument | _: ApexClassMetaDocument =>
// Hack: we don't need to revalidate here but MetadataValidator wipes out all
// diagnostics so if we don't validate we can save to cache without important diagnostics,
// especially the Missing ones which will break invalidation handling.
getDependentType(doc.controllingTypeName(namespace)).foreach(_.validate())
None
case _ => Some(createSupportedTypes(doc, source))
}
}
private def createSupportedTypes(
doc: MetadataDocument,
source: Option[SourceData]
): Seq[DependentType] = {
doc match {
case doc: ApexClassDocument =>
source.flatMap(s => FullDeclaration.create(this, doc, s, forceConstruct = true)).toSeq
case _: ApexTriggerDocument =>
source.flatMap(s => TriggerDeclaration.create(this, doc.path, s)).toSeq
case _: SObjectLike | _: SObjectFieldDocument | _: SObjectFieldSetDocument |
_: SObjectSharingReasonDocument =>
val controllingTypeName = doc.controllingTypeName(namespace)
val docs = index.get(SObjectNature, controllingTypeName)
if (docs.nonEmpty) {
val deployer = new SObjectDeployer(this)
val sobjects = deployer.createSObjects(
SObjectGenerator
.toEvents(docs)
.buffered
)
sobjects.foreach(sobject =>
schemaSObjectType.add(sobject.typeName.name, hasFieldSets = true)
)
sobjects.toIndexedSeq
} else {
Seq()
}
case _: LabelsDocument =>
Seq(createLabelDeclaration())
case _: PageDocument =>
val events = PageGenerator.iterator(index)
val stream = new PackageStream(ArraySeq.unsafeWrapArray(events.toArray))
Seq(PageDeclaration(this).merge(stream))
case _: ComponentDocument =>
val events = ComponentGenerator.iterator(index)
val stream = new PackageStream(ArraySeq.unsafeWrapArray(events.toArray))
Seq(ComponentDeclaration(this).merge(stream))
case _: FlowDocument =>
val events = FlowGenerator.iterator(index)
val stream = new PackageStream(ArraySeq.unsafeWrapArray(events.toArray))
Seq(InterviewDeclaration(this).merge(stream))
}
}
private def checkPathInPackageOrThrow(path: PathLike): Unit = {
if (!index.isVisibleFile(path))
throw new IllegalArgumentException(s"Metadata is not part of this package for '$path'")
}
private def resolveSource(path: PathLike): Option[SourceData] = {
path.readSourceData() match {
case Left(_) => None
case Right(data) => Some(data)
}
}
}