From a0877d81ca2844d75dc361b5ce7c99afacd6e25f Mon Sep 17 00:00:00 2001 From: vlad Date: Thu, 25 Jan 2018 14:12:31 -0800 Subject: Extracting query library --- .../xyz/driver/pdsuicommon/utils/CharOps.scala | 31 --- .../utils/CustomSwaggerJsonFormats.scala | 218 --------------------- .../xyz/driver/pdsuicommon/utils/FutureUtils.scala | 18 -- .../xyz/driver/pdsuicommon/utils/Implicits.scala | 25 --- .../xyz/driver/pdsuicommon/utils/MapOps.scala | 10 - .../xyz/driver/pdsuicommon/utils/RandomUtils.scala | 19 -- .../driver/pdsuicommon/utils/ServiceUtils.scala | 32 --- .../xyz/driver/pdsuicommon/utils/StringOps.scala | 23 --- .../scala/xyz/driver/pdsuicommon/utils/Utils.scala | 43 ---- 9 files changed, 419 deletions(-) delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/CharOps.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/FutureUtils.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/MapOps.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/RandomUtils.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/ServiceUtils.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/StringOps.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/utils/Utils.scala (limited to 'src/main/scala/xyz/driver/pdsuicommon/utils') diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/CharOps.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/CharOps.scala deleted file mode 100644 index cee9c73..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/CharOps.scala +++ /dev/null @@ -1,31 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -final class CharOps(val self: Char) extends AnyVal { - - import CharOps._ - - def isSafeWhitespace: Boolean = Whitespace.matches(self) - - def isSafeControl: Boolean = JavaIsoControl.matches(self) -} - -// From Guava -private object CharOps { - - object Whitespace { - private val Table: String = - "\u2002\u3000\r\u0085\u200A\u2005\u2000\u3000" + - "\u2029\u000B\u3000\u2008\u2003\u205F\u3000\u1680" + - "\u0009\u0020\u2006\u2001\u202F\u00A0\u000C\u2009" + - "\u3000\u2004\u3000\u3000\u2028\n\u2007\u3000" - - private val Multiplier: Int = 1682554634 - private val Shift: Int = Integer.numberOfLeadingZeros(Table.length - 1) - - def matches(c: Char): Boolean = Table.charAt((Multiplier * c) >>> Shift) == c - } - - object JavaIsoControl { - def matches(c: Char): Boolean = c <= '\u001f' || (c >= '\u007f' && c <= '\u009f') - } -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala deleted file mode 100644 index 27560d5..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala +++ /dev/null @@ -1,218 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -import java.time.{LocalDate, LocalDateTime} - -import io.swagger.models.properties.Property -import spray.json.JsValue -import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId} -import xyz.driver.pdsuidomain.entities._ -import xyz.driver.pdsuidomain.formats.json.listresponse._ -import xyz.driver.core.swagger.CustomSwaggerJsonConverter._ -import xyz.driver.entities.patient.CancerType -import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueue -import xyz.driver.pdsuidomain.entities.export.patient.ExportPatientWithLabels -import xyz.driver.pdsuidomain.entities.export.trial.ExportTrialWithLabels -import xyz.driver.pdsuidomain.fakes.entities.common -import xyz.driver.pdsuidomain.formats.json.bridgeuploadqueue._ -import xyz.driver.pdsuidomain.formats.json.record._ -import xyz.driver.pdsuidomain.formats.json.document._ -import xyz.driver.pdsuidomain.services.CriterionService.RichCriterion -import xyz.driver.pdsuidomain.services.ExtractedDataService.RichExtractedData - -import scala.collection.immutable - -object CustomSwaggerJsonFormats { - - trait MedicalRecordListResponse - trait MedicalRecordIssueListResponse - trait MedicalRecordHistoryListResponse - trait DocumentListResponse - trait DocumentIssueListResponse - trait DocumentHistoryListResponse - trait RichExtractedDataListResponse - trait DocumentTypeListResponse - trait ProviderTypeListResponse - - trait TrialListResponse - trait TrialIssueListResponse - trait TrialHistoryListResponse - trait ArmListResponse - trait InterventionWithArmsListResponse - trait EligibilityArmWithDiseasesListResponse - trait SlotArmListResponse - trait RichCriterionListResponse - trait InterventionTypeListResponse - trait StudyDesignListResponse - trait HypothesisListResponse - - trait PatientListResponse - trait PatientIssueListResponse - trait PatientHistoryListResponse - trait PatientLabelListResponse - trait RichPatientLabelListResponse - trait RichPatientCriterionListResponse - trait RichPatientEligibleTrialListResponse - trait PatientHypothesisListResponse - trait PatientLabelEvidenceViewListResponse - - trait QueueUploadItemListResponse - - val customCommonProperties = immutable.Map[Class[_], Property]( - classOf[LocalDateTime] -> stringProperty(example = Some("2010-12-31'T'18:59:59Z")), - classOf[LocalDate] -> stringProperty(example = Some("2010-12-31")), - classOf[UuidId[_]] -> stringProperty(example = Some("370b0450-35cb-4aab-ba74-0145be75add5")), - classOf[StringId[_]] -> stringProperty(), - classOf[LongId[_]] -> stringProperty(), - classOf[CancerType] -> stringProperty() - ) - - val customCommonObjectsExamples = immutable.Map[Class[_], JsValue]( - classOf[BridgeUploadQueue.Item] -> queueUploadItemFormat.write(common.nextBridgeUploadQueueItem()), - classOf[ProviderType] -> providerTypeFormat.write(common.nextProviderType()), - classOf[DocumentType] -> documentTypeFormat.write(common.nextDocumentType()), - classOf[QueueUploadItemListResponse] -> listResponseWriter[BridgeUploadQueue.Item] - .write(common.nextBridgeUploadQueueItemListResponse()), - classOf[DocumentTypeListResponse] -> listResponseWriter[DocumentType].write(common.nextDocumentTypeListResponse()), - classOf[ProviderTypeListResponse] -> listResponseWriter[ProviderType].write(common.nextProviderTypeListResponse()) - ) - - object trialcuration { - import xyz.driver.pdsuidomain.fakes.entities.trialcuration._ - import xyz.driver.pdsuidomain.fakes.entities.export - import xyz.driver.pdsuidomain.formats.json.export._ - import xyz.driver.pdsuidomain.formats.json.arm._ - import xyz.driver.pdsuidomain.formats.json.slotarm._ - import xyz.driver.pdsuidomain.formats.json.eligibilityarm._ - import xyz.driver.pdsuidomain.formats.json.criterion._ - import xyz.driver.pdsuidomain.formats.json.intervention._ - import xyz.driver.pdsuidomain.formats.json.hypothesis._ - import xyz.driver.pdsuidomain.formats.json.studydesign._ - import xyz.driver.pdsuidomain.formats.json.trial._ - import xyz.driver.pdsuidomain.formats.json.trialhistory._ - import xyz.driver.pdsuidomain.formats.json.trialissue._ - - val customTrialCurationProperties = immutable.Map[Class[_], Property]( - classOf[Trial.Status] -> stringProperty(), - classOf[TrialHistory.Action] -> stringProperty(), - classOf[TrialHistory.State] -> stringProperty() - ) ++ customCommonProperties - - val customTrialCurationObjectsExamples = immutable.Map[Class[_], JsValue]( - classOf[Trial] -> trialFormat.write(nextTrial()), - classOf[Arm] -> armFormat.write(nextArm()), - classOf[TrialHistory] -> trialHistoryFormat.write(nextTrialHistory()), - classOf[TrialIssue] -> trialIssueWriter.write(nextTrialIssue()), - classOf[RichCriterion] -> richCriterionFormat.write(nextRichCriterion()), - classOf[InterventionWithArms] -> interventionFormat.write(nextInterventionWithArms()), - classOf[InterventionType] -> interventionTypeFormat.write(nextInterventionType()), - classOf[Hypothesis] -> hypothesisFormat.write(nextHypothesis()), - classOf[StudyDesign] -> studyDesignFormat.write(nextStudyDesign()), - classOf[ExportTrialWithLabels] -> trialWithLabelsFormat.write(export.nextExportTrialWithLabels()), - classOf[EligibilityArmWithDiseases] -> eligibilityArmWithDiseasesWriter.write(nextEligibilityArmWithDiseases()), - classOf[SlotArm] -> slotArmFormat.write(nextSlotArm()), - classOf[TrialListResponse] -> listResponseWriter[Trial].write(nextTrialListResponse()), - classOf[TrialIssueListResponse] -> listResponseWriter[TrialIssue].write(nextTrialIssueListResponse()), - classOf[TrialHistoryListResponse] -> listResponseWriter[TrialHistory].write(nextTrialHistoryListResponse()), - classOf[ArmListResponse] -> listResponseWriter[Arm].write(nextArmListResponse()), - classOf[InterventionWithArmsListResponse] -> listResponseWriter[InterventionWithArms].write( - nextInterventionWithArmsListResponse()), - classOf[EligibilityArmWithDiseasesListResponse] -> listResponseWriter[EligibilityArmWithDiseases].write( - nextEligibilityArmWithDiseasesListResponse()), - classOf[SlotArmListResponse] -> listResponseWriter[SlotArm].write(nextSlotArmListResponse()), - classOf[RichCriterionListResponse] -> listResponseWriter[RichCriterion].write(nextRichCriterionListResponse()), - classOf[InterventionTypeListResponse] -> listResponseWriter[InterventionType].write( - nextInterventionTypeListResponse()), - classOf[StudyDesignListResponse] -> listResponseWriter[StudyDesign].write(nextStudyDesignListResponse()), - classOf[HypothesisListResponse] -> listResponseWriter[Hypothesis].write(nextHypothesesListResponse()) - ) - } - - object recordprocessing { - import xyz.driver.pdsuidomain.fakes.entities.recordprocessing._ - import xyz.driver.pdsuidomain.fakes.entities.export - import xyz.driver.pdsuidomain.formats.json.export._ - import xyz.driver.pdsuidomain.formats.json.documentissue._ - import xyz.driver.pdsuidomain.formats.json.documenthistory._ - import xyz.driver.pdsuidomain.formats.json.recordissue._ - import xyz.driver.pdsuidomain.formats.json.recordhistory._ - import xyz.driver.pdsuidomain.formats.json.extracteddata._ - - val customRecordProcessingProperties = immutable.Map[Class[_], Property]( - classOf[MedicalRecord.Status] -> stringProperty(), - classOf[MedicalRecordHistory.Action] -> stringProperty(), - classOf[MedicalRecordHistory.State] -> stringProperty(), - classOf[Document.Status] -> stringProperty(), - classOf[Document.RequiredType] -> stringProperty(), - classOf[DocumentHistory.Action] -> stringProperty(), - classOf[DocumentHistory.State] -> stringProperty() - ) ++ customCommonProperties - - val customRecordProcessingObjectsExamples = immutable.Map[Class[_], JsValue]( - classOf[Document] -> documentFormat.write(nextDocument()), - classOf[DocumentIssue] -> documentIssueFormat.write(nextDocumentIssue()), - classOf[DocumentHistory] -> documentHistoryFormat.write(nextDocumentHistory()), - classOf[MedicalRecord] -> recordFormat.write(nextMedicalRecord()), - classOf[MedicalRecordIssue] -> recordIssueFormat.write(nextMedicalRecordIssue()), - classOf[MedicalRecordHistory] -> recordHistoryFormat.write(nextMedicalRecordHistory()), - classOf[RichExtractedData] -> extractedDataFormat.write(nextRichExtractedData()), - classOf[ExportPatientWithLabels] -> patientWithLabelsFormat.write(export.nextExportPatientWithLabels()), - classOf[MedicalRecordListResponse] -> listResponseWriter[MedicalRecord].write(nextMedicalRecordListResponse()), - classOf[MedicalRecordIssueListResponse] -> listResponseWriter[MedicalRecordIssue].write( - nextMedicalRecordIssueListResponse()), - classOf[MedicalRecordHistoryListResponse] -> listResponseWriter[MedicalRecordHistory].write( - nextMedicalRecordHistoryListResponse()), - classOf[DocumentListResponse] -> listResponseWriter[Document].write(nextDocumentListResponse()), - classOf[DocumentIssueListResponse] -> listResponseWriter[DocumentIssue].write(nextDocumentIssueListResponse()), - classOf[DocumentHistoryListResponse] -> listResponseWriter[DocumentHistory].write( - nextDocumentHistoryListResponse()), - classOf[RichExtractedDataListResponse] -> listResponseWriter[RichExtractedData].write( - nextRichExtractedDataListResponse()) - ) ++ customCommonObjectsExamples - } - - object treatmentmatching { - import xyz.driver.pdsuidomain.fakes.entities.treatmentmatching._ - import xyz.driver.pdsuidomain.formats.json.patient._ - import xyz.driver.pdsuidomain.formats.json.patientcriterion._ - import xyz.driver.pdsuidomain.formats.json.patientdefiningcriteria._ - import xyz.driver.pdsuidomain.formats.json.patienteligibletrial._ - import xyz.driver.pdsuidomain.formats.json.patientlabel._ - import xyz.driver.pdsuidomain.formats.json.patienthypothesis._ - import xyz.driver.pdsuidomain.formats.json.patienthistory._ - import xyz.driver.pdsuidomain.formats.json.patientissue._ - - val customTreatmentMatchingProperties = immutable.Map[Class[_], Property]( - classOf[Patient.Status] -> stringProperty(), - classOf[PatientHistory.Action] -> stringProperty(), - classOf[PatientHistory.State] -> stringProperty() - ) ++ customCommonProperties - - val customTreatmentMatchingObjectsExamples = immutable.Map[Class[_], JsValue]( - classOf[Patient] -> patientFormat.write(nextPatient()), - classOf[RichPatientLabel] -> richPatientLabelFormat.write(nextRichPatientLabel()), - classOf[PatientLabel] -> patientLabelDefiningCriteriaWriter.write(nextPatientLabel()), - classOf[RichPatientCriterion] -> richPatientCriterionFormat.write(nextRichPatientCriterion()), - classOf[DraftPatientCriterion] -> draftPatientCriterionFormat.write(nextDraftPatientCriterion()), - classOf[PatientLabelEvidenceView] -> patientLabelEvidenceWriter.write(nextPatientLabelEvidenceView()), - classOf[RichPatientEligibleTrial] -> patientEligibleTrialWriter.write(nextRichPatientEligibleTrial()), - classOf[PatientHypothesis] -> patientHypothesisWriter.write(nextPatientHypothesis()), - classOf[PatientHistory] -> patientHistoryFormat.write(nextPatientHistory()), - classOf[PatientIssue] -> patientIssueWriter.write(nextPatientIssue()), - classOf[PatientListResponse] -> listResponseWriter[Patient].write(nextPatientListResponse()), - classOf[PatientLabelListResponse] -> listResponseWriter[PatientLabel].write(nextPatientLabelListResponse()), - classOf[RichPatientLabelListResponse] -> listResponseWriter[RichPatientLabel].write( - nextRichPatientLabelListResponse()), - classOf[RichPatientCriterionListResponse] -> listResponseWriter[RichPatientCriterion].write( - nextRichPatientCriterionListResponse()), - classOf[PatientLabelEvidenceViewListResponse] -> listResponseWriter[PatientLabelEvidenceView].write( - nextPatientLabelEvidenceViewListResponse()), - classOf[RichPatientEligibleTrialListResponse] -> listResponseWriter[RichPatientEligibleTrial].write( - nextRichPatientEligibleTrialListResponse()), - classOf[PatientHypothesisListResponse] -> listResponseWriter[PatientHypothesis].write( - nextPatientHypothesisListResponse()), - classOf[PatientIssueListResponse] -> listResponseWriter[PatientIssue].write(nextPatientIssuesListResponse()), - classOf[PatientHistoryListResponse] -> listResponseWriter[PatientHistory].write(nextPatientHistoryListResponse()) - ) ++ customCommonObjectsExamples - } - -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/FutureUtils.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/FutureUtils.scala deleted file mode 100644 index e8b1f5c..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/FutureUtils.scala +++ /dev/null @@ -1,18 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -import scala.concurrent.{ExecutionContext, Future} -import scala.util.{Failure, Try} - -object FutureUtils { - - def executeSynchronously[T](f: ExecutionContext => Future[T]): Try[T] = { - val future = f { - new ExecutionContext { - override def reportFailure(cause: Throwable): Unit = cause.printStackTrace() - - override def execute(runnable: Runnable): Unit = runnable.run() - } - } - future.value.getOrElse(Failure(new IllegalStateException("Can not evaluate the result of future"))) - } -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala deleted file mode 100644 index 0b18093..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala +++ /dev/null @@ -1,25 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -import scala.collection.generic.CanBuildFrom - -object Implicits { - - final class ConditionalAppend[U, T[U] <: TraversableOnce[U]](val c: T[U]) extends AnyVal { - def condAppend(cond: => Boolean, value: U)(implicit cbf: CanBuildFrom[T[U], U, T[U]]): T[U] = { - val col = cbf() - if (cond) { - ((col ++= c) += value).result - } else { - c.asInstanceOf[T[U]] - } - } - } - - implicit def traversableConditionalAppend[U, T[U] <: TraversableOnce[U]](c: T[U]): ConditionalAppend[U, T] = - new ConditionalAppend[U, T](c) - - implicit def toMapOps[K, V](x: Map[K, V]): MapOps[K, V] = new MapOps(x) - - implicit def toCharOps(self: Char): CharOps = new CharOps(self) - implicit def toStringOps(self: String): StringOps = new StringOps(self) -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/MapOps.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/MapOps.scala deleted file mode 100644 index ac9a162..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/MapOps.scala +++ /dev/null @@ -1,10 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -final class MapOps[K, V](val self: Map[K, V]) extends AnyVal { - - def swap: Map[V, Set[K]] = { - self.toList - .groupBy { case (_, v) => v } - .mapValues(_.map { case (k, _) => k }.toSet) - } -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/RandomUtils.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/RandomUtils.scala deleted file mode 100644 index faf8703..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/RandomUtils.scala +++ /dev/null @@ -1,19 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -import java.util.concurrent.ThreadLocalRandom - -import scala.collection._ - -object RandomUtils { - - private def Random = ThreadLocalRandom.current() - - private val chars: Seq[Char] = ('0' to '9') ++ ('a' to 'z') - - def randomString(len: Int): String = { - (0 until len).map({ _ => - val i = Random.nextInt(0, chars.size) - chars(i) - })(breakOut) - } -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/ServiceUtils.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/ServiceUtils.scala deleted file mode 100644 index 68070f4..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/ServiceUtils.scala +++ /dev/null @@ -1,32 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -import xyz.driver.pdsuicommon.db.SearchFilterBinaryOperation.Eq -import xyz.driver.pdsuicommon.db.SearchFilterExpr -import xyz.driver.pdsuicommon.db.SearchFilterExpr.{Atom, Dimension} -import xyz.driver.pdsuicommon.logging._ - -import scala.util.{Failure, Success, Try} - -object ServiceUtils extends PhiLogging { - - def findEqFilter(filter: SearchFilterExpr, fieldName: String): Option[SearchFilterExpr] = { - findEqFilter(filter, Dimension(None, fieldName)) - } - - def findEqFilter(filter: SearchFilterExpr, dimension: Dimension): Option[SearchFilterExpr] = { - filter.find { - case Atom.Binary(`dimension`, Eq, _) => true - case _ => false - } - } - - def convertIdInFilterToLong(value: AnyRef): Option[Long] = { - Try(value.toString.toLong) match { - case Success(id) => - Option(id) - case Failure(e) => - logger.error(phi"Incorrect id format in filter $e") - None - } - } -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/StringOps.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/StringOps.scala deleted file mode 100644 index eaac761..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/StringOps.scala +++ /dev/null @@ -1,23 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -import xyz.driver.pdsuicommon.utils.Implicits.toCharOps - -final class StringOps(val self: String) extends AnyVal { - - def safeTrim: String = { - def shouldKeep(c: Char): Boolean = !c.isSafeControl && !c.isSafeWhitespace - - if (self.isEmpty) { - "" - } else { - val start = self.indexWhere(shouldKeep) - val end = self.lastIndexWhere(shouldKeep) - - if (start >= 0 && end >= 0) { - self.substring(start, end + 1) - } else { - "" - } - } - } -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/Utils.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/Utils.scala deleted file mode 100644 index 63b0572..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/utils/Utils.scala +++ /dev/null @@ -1,43 +0,0 @@ -package xyz.driver.pdsuicommon.utils - -import java.time.LocalDateTime -import java.util.regex.{Matcher, Pattern} - -object Utils { - - implicit val localDateTimeOrdering: Ordering[LocalDateTime] = Ordering.fromLessThan(_ isBefore _) - - /** - * Hack to avoid scala compiler bug with getSimpleName - * @see https://issues.scala-lang.org/browse/SI-2034 - */ - def getClassSimpleName(klass: Class[_]): String = { - try { - klass.getSimpleName - } catch { - case _: InternalError => - val fullName = klass.getName.stripSuffix("$") - val fullClassName = fullName.substring(fullName.lastIndexOf(".") + 1) - fullClassName.substring(fullClassName.lastIndexOf("$") + 1) - } - } - - def toSnakeCase(str: String): String = - str - .replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2") - .replaceAll("([a-z\\d])([A-Z])", "$1_$2") - .toLowerCase - - def toCamelCase(str: String): String = { - val sb = new StringBuffer() - def loop(m: Matcher): Unit = if (m.find()) { - m.appendReplacement(sb, m.group(1).toUpperCase()) - loop(m) - } - val m: Matcher = Pattern.compile("_(.)").matcher(str) - loop(m) - m.appendTail(sb) - sb.toString - } - -} -- cgit v1.2.3