From a5fdfd34d44f2dd369afe5782a46364eeeebcb5e Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 27 Oct 2026 19:14:22 -0700 Subject: [PATCH] Move single-module classes out of api into their owning modules --- flow/src/org/labkey/flow/FlowModule.java | 2 +- flow/src/org/labkey/flow/FlowServiceImpl.java | 10 +- flow/src/org/labkey/flow/api/FlowService.java | 39 ++ .../executescript/SamplesConfirmGridView.java | 2 +- .../executescript/ScriptOverview.java | 2 +- .../src/org/labkey/flow/data/InputColumn.java | 44 ++ flow/src/org/labkey/flow/view/Overview.java | 314 +++++++++++++ .../labkey/flow/webparts/FlowOverview.java | 2 +- .../labkey/flow/webparts/OverviewWebPart.java | 1 + ms2/src/org/labkey/ms2/DeltaScanColumn.java | 2 +- ms2/src/org/labkey/ms2/MS2Controller.java | 2 +- ms2/src/org/labkey/ms2/PeptideManager.java | 2 +- .../org/labkey/ms2/ProteinProphetFile.java | 2 +- .../labkey/ms2/ProteinProphetImporter.java | 4 +- .../ms2/action/SimpleForwardAction.java | 51 +++ ms2/src/org/labkey/ms2/arrays/FloatArray.java | 115 +++++ .../ms2/peptideview/SingleMS2RunRReport.java | 4 +- .../labkey/ms2/reader/LibraQuantHandler.java | 1 - .../org/labkey/ms2/reader/MS2XmlLoader.java | 3 +- .../labkey/ms2/reader/MzMLDocumentParser.java | 2 +- .../ms2/reader/MzXMLDocumentParser.java | 2 +- .../reader/PepXmlAnalysisResultHandler.java | 1 - .../org/labkey/ms2/reader/PepXmlLoader.java | 1 - .../ms2/reader/PeptideProphetHandler.java | 1 - .../ms2/reader/PeptideProphetSummary.java | 3 +- .../org/labkey/ms2/reader/ProtXmlReader.java | 3 +- .../org/labkey/ms2/reader/ProteinGroup.java | 1 - .../labkey/ms2/reader/Q3AnalysisSummary.java | 1 - ms2/src/org/labkey/ms2/reader/Q3Handler.java | 1 - .../ms2/reader/SequentialMzxmlIterator.java | 5 +- .../ms2/reader/SimpleXMLStreamReader.java | 77 ++++ .../org/labkey/ms2/reader/TarIterator.java | 2 +- .../ms2/reader/XMLStreamReaderWrapper.java | 308 +++++++++++++ .../ms2/reader/XPressAnalysisSummary.java | 1 - .../org/labkey/ms2/reader/XPressHandler.java | 1 - .../ms2/reports/report/r/CustomRReport.java | 111 +++++ .../ms2/search/AbstractXMLDocumentParser.java | 158 +++++++ .../ms2/settings/PreferenceService.java | 219 +++++++++ ms2/src/org/labkey/ms2/util/MatrixUtil.java | 148 ++++++ ...PossiblyGZIPpedFileInputStreamFactory.java | 60 +++ .../org/labkey/nab/query/NabRunDataTable.java | 1 - .../nab/query/QcAwarePropertyForeignKey.java | 422 ++++++++++++++++++ .../protein/view/proteinCoverageMap.jsp | 2 +- .../protein/visualization/ColorGradient.java | 77 ++++ 44 files changed, 2170 insertions(+), 40 deletions(-) create mode 100644 flow/src/org/labkey/flow/api/FlowService.java create mode 100644 flow/src/org/labkey/flow/data/InputColumn.java create mode 100644 flow/src/org/labkey/flow/view/Overview.java create mode 100644 ms2/src/org/labkey/ms2/action/SimpleForwardAction.java create mode 100644 ms2/src/org/labkey/ms2/arrays/FloatArray.java create mode 100644 ms2/src/org/labkey/ms2/reader/SimpleXMLStreamReader.java create mode 100644 ms2/src/org/labkey/ms2/reader/XMLStreamReaderWrapper.java create mode 100644 ms2/src/org/labkey/ms2/reports/report/r/CustomRReport.java create mode 100644 ms2/src/org/labkey/ms2/search/AbstractXMLDocumentParser.java create mode 100644 ms2/src/org/labkey/ms2/settings/PreferenceService.java create mode 100644 ms2/src/org/labkey/ms2/util/MatrixUtil.java create mode 100644 ms2/src/org/labkey/ms2/util/PossiblyGZIPpedFileInputStreamFactory.java create mode 100644 nab/src/org/labkey/nab/query/QcAwarePropertyForeignKey.java create mode 100644 protein/src/org/labkey/protein/visualization/ColorGradient.java diff --git a/flow/src/org/labkey/flow/FlowModule.java b/flow/src/org/labkey/flow/FlowModule.java index 4427a90d02..f18bd8e014 100644 --- a/flow/src/org/labkey/flow/FlowModule.java +++ b/flow/src/org/labkey/flow/FlowModule.java @@ -26,7 +26,6 @@ import org.labkey.api.exp.api.ExperimentService; import org.labkey.api.files.FileContentService; import org.labkey.api.files.TableUpdaterFileListener; -import org.labkey.api.flow.api.FlowService; import org.labkey.api.module.FolderTypeManager; import org.labkey.api.module.Module; import org.labkey.api.module.ModuleContext; @@ -56,6 +55,7 @@ import org.labkey.flow.analysis.web.StatisticSpec; import org.labkey.flow.analysis.web.SubsetParser; import org.labkey.flow.analysis.web.SubsetTests; +import org.labkey.flow.api.FlowService; import org.labkey.flow.controllers.FlowController; import org.labkey.flow.controllers.ReportsController; import org.labkey.flow.controllers.attribute.AttributeController; diff --git a/flow/src/org/labkey/flow/FlowServiceImpl.java b/flow/src/org/labkey/flow/FlowServiceImpl.java index 11e294d7eb..5cab72f57a 100644 --- a/flow/src/org/labkey/flow/FlowServiceImpl.java +++ b/flow/src/org/labkey/flow/FlowServiceImpl.java @@ -17,13 +17,13 @@ package org.labkey.flow; import org.jetbrains.annotations.Nullable; -import org.labkey.api.data.Container; -import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.Container; +import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SqlSelector; import org.labkey.api.exp.api.ExpData; -import org.labkey.api.exp.api.ExperimentService; -import org.labkey.api.flow.api.FlowService; +import org.labkey.api.exp.api.ExperimentService; import org.labkey.api.util.FileUtil; +import org.labkey.flow.api.FlowService; import org.labkey.flow.persist.FlowManager; import java.nio.file.Path; @@ -31,7 +31,7 @@ import java.util.Collections; import java.util.List; - + /** * User: matthewb * Date: Oct 25, 2010 diff --git a/flow/src/org/labkey/flow/api/FlowService.java b/flow/src/org/labkey/flow/api/FlowService.java new file mode 100644 index 0000000000..2a51b96328 --- /dev/null +++ b/flow/src/org/labkey/flow/api/FlowService.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2026 LabKey Corporation + * + * 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.labkey.flow.api; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.services.ServiceRegistry; +import org.labkey.api.webdav.WebdavResourceExpDataProvider; + +/** + * User: matthewb + * Date: Oct 25, 2010 + * Time: 10:18:04 AM + */ +public interface FlowService extends WebdavResourceExpDataProvider +{ + static @Nullable FlowService get() + { + return ServiceRegistry.get().getService(FlowService.class); + } + + static void setInstance(FlowService impl) + { + ServiceRegistry.get().registerService(FlowService.class, impl); + } +} diff --git a/flow/src/org/labkey/flow/controllers/executescript/SamplesConfirmGridView.java b/flow/src/org/labkey/flow/controllers/executescript/SamplesConfirmGridView.java index 272abe9fc8..95e307b44b 100644 --- a/flow/src/org/labkey/flow/controllers/executescript/SamplesConfirmGridView.java +++ b/flow/src/org/labkey/flow/controllers/executescript/SamplesConfirmGridView.java @@ -32,7 +32,6 @@ import org.labkey.api.data.Container; import org.labkey.api.data.DataRegion; import org.labkey.api.data.DisplayColumn; -import org.labkey.api.data.InputColumn; import org.labkey.api.data.JdbcType; import org.labkey.api.data.RenderContext; import org.labkey.api.data.Results; @@ -55,6 +54,7 @@ import org.labkey.flow.analysis.model.Workspace; import org.labkey.flow.data.FlowFCSFile; import org.labkey.flow.data.FlowRun; +import org.labkey.flow.data.InputColumn; import org.labkey.flow.query.FlowSchema; import org.labkey.flow.util.KeywordUtil; import org.springframework.validation.Errors; diff --git a/flow/src/org/labkey/flow/controllers/executescript/ScriptOverview.java b/flow/src/org/labkey/flow/controllers/executescript/ScriptOverview.java index 413e870cf6..5068f5f6c9 100644 --- a/flow/src/org/labkey/flow/controllers/executescript/ScriptOverview.java +++ b/flow/src/org/labkey/flow/controllers/executescript/ScriptOverview.java @@ -22,7 +22,6 @@ import org.labkey.api.util.HtmlString; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; -import org.labkey.api.view.Overview; import org.labkey.flow.analysis.model.Analysis; import org.labkey.flow.analysis.model.CompensationCalculation; import org.labkey.flow.analysis.model.Population; @@ -32,6 +31,7 @@ import org.labkey.flow.data.FlowProtocolStep; import org.labkey.flow.data.FlowRun; import org.labkey.flow.data.FlowScript; +import org.labkey.flow.view.Overview; import java.util.List; diff --git a/flow/src/org/labkey/flow/data/InputColumn.java b/flow/src/org/labkey/flow/data/InputColumn.java new file mode 100644 index 0000000000..a705242edd --- /dev/null +++ b/flow/src/org/labkey/flow/data/InputColumn.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012-2026 LabKey Corporation + * + * 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.labkey.flow.data; + +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.DataColumn; +import org.labkey.api.data.RenderContext; +import org.labkey.api.writer.HtmlWriter; + +/** + * Renders a bound ColumnInfo as an HTML form input in a grid view. + */ +public class InputColumn extends DataColumn +{ + public InputColumn(ColumnInfo col) + { + super(col); + } + + @Override + public void renderGridCellContents(RenderContext ctx, HtmlWriter out) + { + renderInputHtml(ctx, out, getInputValue(ctx)); + } + + @Override + public void render(RenderContext ctx, HtmlWriter out) + { + renderInputHtml(ctx, out, getInputValue(ctx)); + } +} diff --git a/flow/src/org/labkey/flow/view/Overview.java b/flow/src/org/labkey/flow/view/Overview.java new file mode 100644 index 0000000000..b3ab88cac2 --- /dev/null +++ b/flow/src/org/labkey/flow/view/Overview.java @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2008-2026 LabKey Corporation + * + * 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.labkey.flow.view; + +import org.apache.commons.lang3.StringUtils; +import org.labkey.api.data.Container; +import org.labkey.api.security.User; +import org.labkey.api.security.permissions.Permission; +import org.labkey.api.util.HasHtmlString; +import org.labkey.api.util.HtmlString; +import org.labkey.api.util.HtmlStringBuilder; +import org.labkey.api.util.LinkBuilder; +import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.view.ActionURL; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class for rendering a series of steps involved with setting something up. + * Steps may be enabled or disabled. If disabled, they are shown as grey. + */ +public class Overview implements HasHtmlString +{ + private final User _user; + private final Container _container; + private final List _steps; + private final List _actions; + + private String _title; + private HtmlString _explanatoryHTML; + private HtmlString _statusHTML; + + public Overview(User user, Container container) + { + _user = user; + _container = container; + _steps = new ArrayList<>(); + _actions = new ArrayList<>(); + } + + static protected String h(Object text) + { + return PageFlowUtil.filter(text); + } + + @Override + public HtmlString getHtmlString() + { + HtmlStringBuilder ret = HtmlStringBuilder.of(); + ret.unsafeAppend("
\n"); + if (!StringUtils.isEmpty(_title)) + { + ret.unsafeAppend(""); + ret.append(_title); + ret.unsafeAppend("
\n"); + } + if (!HtmlString.isEmpty(_explanatoryHTML)) + { + ret.unsafeAppend(""); + ret.append(_explanatoryHTML); + ret.unsafeAppend("
\n"); + } + + if (!HtmlString.isEmpty(_statusHTML)) + { + ret.append(_statusHTML); + ret.unsafeAppend("
"); + } + if (!_steps.isEmpty()) + { + ret.unsafeAppend("
    "); + for (Step step : _steps) + { + ret.append(step); + } + ret.unsafeAppend("
"); + } + for (Action miscAction : _actions) + { + ret.append(miscAction); + ret.unsafeAppend("
"); + } + ret.unsafeAppend("
"); + return ret.getHtmlString(); + } + + @Override + public String toString() + { + return getHtmlString().toString(); + } + + + public void addStep(Step step) + { + _steps.add(step); + } + + public void addAction(Action action) + { + _actions.add(action); + } + + public boolean hasPermission(Class perm) + { + return _container.hasPermission(_user, perm); + } + + public User getUser() + { + return _user; + } + + public Container getContainer() + { + return _container; + } + + /** + * Sets the title. The title appears at the top. + */ + public void setTitle(String title) + { + _title = title; + } + + /** + * Set the explanatory text which appears at the top. + * Explanatory text is text that is intended to be ignored by users that know what they're doing. It is + * rendered in italics. + */ + public void setExplanatoryHTML(HtmlString html) + { + _explanatoryHTML = html; + } + + /** + * Set the status text which appears at the top. + * Status text sometimes changes and contains information that may be useful even to people who know how to use the product. + */ + public void setStatusHTML(HtmlString html) + { + _statusHTML = html; + } + + public static class Step implements HasHtmlString + { + public enum Status + { + normal, + required, + optional, + disabled, + completed, + } + + private final String _title; + private final Status _status; + private final List _actions; + + private HtmlString _expanatoryHTML; + private HtmlString _statusHTML; + + public Step(String title, Status status) + { + _title = title; + _status = status; + _actions = new ArrayList<>(); + } + + public void setExplanatoryHTML(HtmlString html) + { + _expanatoryHTML = html; + } + + public void setStatusHTML(HtmlString html) + { + _statusHTML = html; + } + + public void addAction(Action action) + { + if (action == null) + return; + _actions.add(action); + } + + @Override + public HtmlString getHtmlString() + { + HtmlStringBuilder ret = HtmlStringBuilder.of(); + ret.unsafeAppend("
  • "); + ret.unsafeAppend(""); + ret.append(_title); + ret.unsafeAppend(""); + if (_status == Status.optional) + { + ret.append(" (optional)"); + } + if (_status == Status.completed) + { + ret.append(" (completed)"); + } + if (_status == Status.required) + { + ret.append(" (required)"); + } + if (!HtmlString.isEmpty(_expanatoryHTML)) + { + ret.unsafeAppend("
    "); + ret.append(_expanatoryHTML); + ret.unsafeAppend(""); + } + if (!(HtmlString.isEmpty(_statusHTML))) + { + ret.unsafeAppend("
    "); + ret.append(_statusHTML); + } + for (Action action : _actions) + { + ret.unsafeAppend("
    \n"); + ret.append(action); + } + ret.unsafeAppend("
  • "); + return ret.getHtmlString(); + } + + @Override + public String toString() + { + return getHtmlString().toString(); + } + } + + public static class Action implements HasHtmlString + { + private final String _label; + private final ActionURL _url; + + private HtmlString _explanatoryHTML; + private HtmlString _descriptionHTML; + + public Action(String label, ActionURL url) + { + _label = label; + _url = url; + } + + public void setExplanatoryHTML(HtmlString html) + { + _explanatoryHTML = html; + } + + public void setDescriptionHTML(HtmlString html) + { + _descriptionHTML = html; + } + + @Override + public HtmlString getHtmlString() + { + HtmlStringBuilder ret = HtmlStringBuilder.of(); + ret.unsafeAppend(""); + if (!HtmlString.isEmpty(_explanatoryHTML)) + { + ret.unsafeAppend(""); + ret.append(_explanatoryHTML); + ret.unsafeAppend("
    "); + } + if (!HtmlString.isEmpty(_descriptionHTML)) + { + ret.append(_descriptionHTML); + ret.unsafeAppend("
    "); + } + ret.unsafeAppend(""); + if (_url != null) + { + ret.append(LinkBuilder.labkeyLink(_label, _url)); + } + else + { + ret.append(_label); + } + ret.unsafeAppend("
    "); + return ret.getHtmlString(); + } + + @Override + public String toString() + { + return getHtmlString().toString(); + } + } +} diff --git a/flow/src/org/labkey/flow/webparts/FlowOverview.java b/flow/src/org/labkey/flow/webparts/FlowOverview.java index 18290e1a37..34de7070be 100644 --- a/flow/src/org/labkey/flow/webparts/FlowOverview.java +++ b/flow/src/org/labkey/flow/webparts/FlowOverview.java @@ -32,7 +32,6 @@ import org.labkey.api.util.HtmlStringBuilder; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.ActionURL; -import org.labkey.api.view.Overview; import org.labkey.flow.controllers.executescript.AnalysisScriptController; import org.labkey.flow.controllers.protocol.ProtocolController; import org.labkey.flow.controllers.run.RunController; @@ -42,6 +41,7 @@ import org.labkey.flow.persist.FlowManager; import org.labkey.flow.persist.ObjectType; import org.labkey.flow.query.FlowTableType; +import org.labkey.flow.view.Overview; public class FlowOverview extends Overview { diff --git a/flow/src/org/labkey/flow/webparts/OverviewWebPart.java b/flow/src/org/labkey/flow/webparts/OverviewWebPart.java index 16a430902f..ea3d5707a2 100644 --- a/flow/src/org/labkey/flow/webparts/OverviewWebPart.java +++ b/flow/src/org/labkey/flow/webparts/OverviewWebPart.java @@ -19,6 +19,7 @@ import org.labkey.api.util.HtmlString; import org.labkey.api.view.*; import org.labkey.flow.controllers.FlowController; +import org.labkey.flow.view.Overview; public class OverviewWebPart extends HtmlView { diff --git a/ms2/src/org/labkey/ms2/DeltaScanColumn.java b/ms2/src/org/labkey/ms2/DeltaScanColumn.java index 77528cdcae..51e0e36f42 100644 --- a/ms2/src/org/labkey/ms2/DeltaScanColumn.java +++ b/ms2/src/org/labkey/ms2/DeltaScanColumn.java @@ -24,7 +24,7 @@ import org.labkey.api.data.SqlSelector; import org.labkey.api.query.FieldKey; import org.labkey.api.query.QueryService; -import org.labkey.api.util.MatrixUtil; +import org.labkey.ms2.util.MatrixUtil; import java.util.ArrayList; import java.util.HashMap; diff --git a/ms2/src/org/labkey/ms2/MS2Controller.java b/ms2/src/org/labkey/ms2/MS2Controller.java index 3a234ecd04..4a3fda3f6d 100644 --- a/ms2/src/org/labkey/ms2/MS2Controller.java +++ b/ms2/src/org/labkey/ms2/MS2Controller.java @@ -37,7 +37,6 @@ import org.labkey.api.action.QueryViewAction; import org.labkey.api.action.ReturnUrlForm; import org.labkey.api.action.SimpleErrorView; -import org.labkey.api.action.SimpleForwardAction; import org.labkey.api.action.SimpleRedirectAction; import org.labkey.api.action.SimpleViewAction; import org.labkey.api.action.SpringActionController; @@ -151,6 +150,7 @@ import org.labkey.api.view.WebPartView; import org.labkey.api.view.template.PageConfig; import org.labkey.api.writer.HtmlWriter; +import org.labkey.ms2.action.SimpleForwardAction; import org.labkey.ms2.compare.CompareDataRegion; import org.labkey.ms2.compare.CompareExcelWriter; import org.labkey.ms2.compare.CompareQuery; diff --git a/ms2/src/org/labkey/ms2/PeptideManager.java b/ms2/src/org/labkey/ms2/PeptideManager.java index 3544128f6d..0a913c98e0 100644 --- a/ms2/src/org/labkey/ms2/PeptideManager.java +++ b/ms2/src/org/labkey/ms2/PeptideManager.java @@ -30,12 +30,12 @@ import org.labkey.api.protein.SimpleProtein; import org.labkey.api.query.FieldKey; import org.labkey.api.security.User; -import org.labkey.api.settings.PreferenceService; import org.labkey.api.util.logging.LogHelper; import org.labkey.api.view.ActionURL; import org.labkey.api.view.NotFoundException; import org.labkey.ms2.protein.Protein; import org.labkey.ms2.protein.ProteinViewBean; +import org.labkey.ms2.settings.PreferenceService; import java.text.DecimalFormat; import java.text.NumberFormat; diff --git a/ms2/src/org/labkey/ms2/ProteinProphetFile.java b/ms2/src/org/labkey/ms2/ProteinProphetFile.java index c715104c7b..dee16c3dde 100644 --- a/ms2/src/org/labkey/ms2/ProteinProphetFile.java +++ b/ms2/src/org/labkey/ms2/ProteinProphetFile.java @@ -16,9 +16,9 @@ package org.labkey.ms2; -import org.labkey.api.reader.SimpleXMLStreamReader; import org.labkey.ms2.reader.PeptideProphetSummary; import org.labkey.ms2.reader.SensitivitySummary; +import org.labkey.ms2.reader.SimpleXMLStreamReader; import javax.xml.stream.XMLStreamException; import java.util.ArrayList; diff --git a/ms2/src/org/labkey/ms2/ProteinProphetImporter.java b/ms2/src/org/labkey/ms2/ProteinProphetImporter.java index 41be385f3a..bcbb0ee834 100644 --- a/ms2/src/org/labkey/ms2/ProteinProphetImporter.java +++ b/ms2/src/org/labkey/ms2/ProteinProphetImporter.java @@ -29,17 +29,17 @@ import org.labkey.api.protein.ProteinSchema; import org.labkey.api.protein.fasta.FastaProtein; import org.labkey.api.query.AliasManager; -import org.labkey.api.reader.SimpleXMLStreamReader; import org.labkey.api.util.FileUtil; import org.labkey.api.util.NetworkDrive; import org.labkey.api.util.Pair; -import org.labkey.api.util.PossiblyGZIPpedFileInputStreamFactory; import org.labkey.api.util.StringUtilsLabKey; import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.ms2.pipeline.TPPTask; import org.labkey.ms2.reader.ITraqProteinQuantitation; import org.labkey.ms2.reader.ProtXmlReader; import org.labkey.ms2.reader.ProteinGroup; +import org.labkey.ms2.reader.SimpleXMLStreamReader; +import org.labkey.ms2.util.PossiblyGZIPpedFileInputStreamFactory; import org.labkey.vfs.FileLike; import org.labkey.vfs.FileSystemLike; diff --git a/ms2/src/org/labkey/ms2/action/SimpleForwardAction.java b/ms2/src/org/labkey/ms2/action/SimpleForwardAction.java new file mode 100644 index 0000000000..e6300b0a8e --- /dev/null +++ b/ms2/src/org/labkey/ms2/action/SimpleForwardAction.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2008-2026 LabKey Corporation + * + * 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.labkey.ms2.action; + +import org.labkey.api.action.SimpleViewAction; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.NavTree; +import org.labkey.api.view.ViewServlet; +import org.springframework.validation.BindException; +import org.springframework.web.servlet.ModelAndView; + +/** + * Similar to {@link org.labkey.api.action.SimpleRedirectAction}, but forwards to the URL instead of redirecting. + * This preserves post data, etc because it's handled internally within the web server instead of asking the browser + * to send the request to a different URL + */ +public abstract class SimpleForwardAction
    extends SimpleViewAction +{ + @Override + public ModelAndView getView(FORM form, BindException errors) throws Exception + { + ViewServlet.forwardActionURL(getViewContext().getRequest(), getViewContext().getResponse(), getForwardURL(form)); + return null; + } + + @Override + public void addNavTrail(NavTree root) + { + } + + public abstract ActionURL getForwardURL(FORM form) throws Exception; + + @Override + protected String getCommandClassMethodName() + { + return "getForwardURL"; + } +} \ No newline at end of file diff --git a/ms2/src/org/labkey/ms2/arrays/FloatArray.java b/ms2/src/org/labkey/ms2/arrays/FloatArray.java new file mode 100644 index 0000000000..16e3d95d84 --- /dev/null +++ b/ms2/src/org/labkey/ms2/arrays/FloatArray.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2004-2026 Fred Hutchinson Cancer Research Center + * + * 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.labkey.ms2.arrays; + +import java.util.ArrayList; + + +/** + * Alternative to ArrayList + * User: mbellew + * Date: May 24, 2004 + */ +public class FloatArray +{ + private static final int ARRAY_LEN = 1024; + + int _lenSegment = ARRAY_LEN; + ArrayList _list = new ArrayList<>(); + float[] _arrayLast; + int _lenLast = 0; + int _size = 0; + + + public FloatArray() + { + _arrayLast = new float[_lenSegment]; + _list.add(_arrayLast); + } + + + private FloatArray(float[] a) + { + _list = new ArrayList<>(1); + _arrayLast = a; + _list.add(a); + _size = a.length; + _lenSegment = _size; + _lenLast = _size; + } + + + public void add(float f) + { + if (_arrayLast.length <= _lenLast) + { + _arrayLast = new float[_lenSegment]; + _list.add(_arrayLast); + _lenLast = 0; + } + _arrayLast[_lenLast++] = f; + _size++; + } + + + public float get(int i) + { + return _list.get(i / _lenSegment)[i % _lenSegment]; + } + + + public int size() + { + return _size; + } + + public void setSize(int newSize) + { + _size=newSize; + } + + + public float[] toArray(float[] dst) + { +// assert null == "not tested"; + if (null == dst || dst.length < _size) + dst = new float[_size]; + int end=0, i=0; + float[] src; + for (; i<_list.size()-1 ; i++) + { + src = _list.get(i); + System.arraycopy(src, 0, dst, end, src.length); + end += src.length; + } + src = _list.get(i); + System.arraycopy(src, 0, dst, end, _lenLast); + return dst; +} + + + public static FloatArray asFloatArray(float[] a) + { + return new FloatArray(a) + { + @Override + public void add(float f) + { + throw new java.lang.UnsupportedOperationException(); + } + }; + } +} diff --git a/ms2/src/org/labkey/ms2/peptideview/SingleMS2RunRReport.java b/ms2/src/org/labkey/ms2/peptideview/SingleMS2RunRReport.java index a78529dd1e..94ce3d3d30 100644 --- a/ms2/src/org/labkey/ms2/peptideview/SingleMS2RunRReport.java +++ b/ms2/src/org/labkey/ms2/peptideview/SingleMS2RunRReport.java @@ -15,14 +15,14 @@ */ package org.labkey.ms2.peptideview; -import org.labkey.api.reports.report.r.CustomRReport; import org.labkey.api.query.QueryView; +import org.labkey.api.view.ActionURL; import org.labkey.api.view.NotFoundException; import org.labkey.api.view.ViewContext; -import org.labkey.api.view.ActionURL; import org.labkey.ms2.MS2Run; import org.labkey.ms2.MS2Manager; import org.labkey.ms2.MS2Controller; +import org.labkey.ms2.reports.report.r.CustomRReport; /** * User: jeckels diff --git a/ms2/src/org/labkey/ms2/reader/LibraQuantHandler.java b/ms2/src/org/labkey/ms2/reader/LibraQuantHandler.java index 4650ad706b..5c995f705a 100644 --- a/ms2/src/org/labkey/ms2/reader/LibraQuantHandler.java +++ b/ms2/src/org/labkey/ms2/reader/LibraQuantHandler.java @@ -15,7 +15,6 @@ */ package org.labkey.ms2.reader; -import org.labkey.api.reader.SimpleXMLStreamReader; import javax.xml.stream.XMLStreamException; diff --git a/ms2/src/org/labkey/ms2/reader/MS2XmlLoader.java b/ms2/src/org/labkey/ms2/reader/MS2XmlLoader.java index 2e0c3cbe1c..6a51728924 100644 --- a/ms2/src/org/labkey/ms2/reader/MS2XmlLoader.java +++ b/ms2/src/org/labkey/ms2/reader/MS2XmlLoader.java @@ -17,8 +17,7 @@ package org.labkey.ms2.reader; import org.apache.logging.log4j.Logger; -import org.labkey.api.reader.SimpleXMLStreamReader; -import org.labkey.api.util.PossiblyGZIPpedFileInputStreamFactory; +import org.labkey.ms2.util.PossiblyGZIPpedFileInputStreamFactory; import org.labkey.vfs.FileLike; import javax.xml.stream.XMLStreamException; diff --git a/ms2/src/org/labkey/ms2/reader/MzMLDocumentParser.java b/ms2/src/org/labkey/ms2/reader/MzMLDocumentParser.java index 587cc1674d..5e5644e7ee 100644 --- a/ms2/src/org/labkey/ms2/reader/MzMLDocumentParser.java +++ b/ms2/src/org/labkey/ms2/reader/MzMLDocumentParser.java @@ -15,8 +15,8 @@ */ package org.labkey.ms2.reader; -import org.labkey.api.search.AbstractXMLDocumentParser; import org.labkey.api.webdav.WebdavResource; +import org.labkey.ms2.search.AbstractXMLDocumentParser; import org.xml.sax.ContentHandler; import org.xml.sax.helpers.DefaultHandler; diff --git a/ms2/src/org/labkey/ms2/reader/MzXMLDocumentParser.java b/ms2/src/org/labkey/ms2/reader/MzXMLDocumentParser.java index 03856e267f..6e10253f84 100644 --- a/ms2/src/org/labkey/ms2/reader/MzXMLDocumentParser.java +++ b/ms2/src/org/labkey/ms2/reader/MzXMLDocumentParser.java @@ -15,8 +15,8 @@ */ package org.labkey.ms2.reader; -import org.labkey.api.search.AbstractXMLDocumentParser; import org.labkey.api.webdav.WebdavResource; +import org.labkey.ms2.search.AbstractXMLDocumentParser; import org.xml.sax.ContentHandler; import org.xml.sax.helpers.DefaultHandler; diff --git a/ms2/src/org/labkey/ms2/reader/PepXmlAnalysisResultHandler.java b/ms2/src/org/labkey/ms2/reader/PepXmlAnalysisResultHandler.java index 05d4dd9d84..559676f092 100644 --- a/ms2/src/org/labkey/ms2/reader/PepXmlAnalysisResultHandler.java +++ b/ms2/src/org/labkey/ms2/reader/PepXmlAnalysisResultHandler.java @@ -15,7 +15,6 @@ */ package org.labkey.ms2.reader; -import org.labkey.api.reader.SimpleXMLStreamReader; import javax.xml.stream.XMLStreamException; import java.util.Map; diff --git a/ms2/src/org/labkey/ms2/reader/PepXmlLoader.java b/ms2/src/org/labkey/ms2/reader/PepXmlLoader.java index 0ea210d931..8ff8254a2e 100644 --- a/ms2/src/org/labkey/ms2/reader/PepXmlLoader.java +++ b/ms2/src/org/labkey/ms2/reader/PepXmlLoader.java @@ -20,7 +20,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.labkey.api.protein.fasta.FastaProtein; -import org.labkey.api.reader.SimpleXMLStreamReader; import org.labkey.ms2.MS2Modification; import org.labkey.vfs.FileLike; diff --git a/ms2/src/org/labkey/ms2/reader/PeptideProphetHandler.java b/ms2/src/org/labkey/ms2/reader/PeptideProphetHandler.java index 8b42fd42d0..16ea9acb5a 100644 --- a/ms2/src/org/labkey/ms2/reader/PeptideProphetHandler.java +++ b/ms2/src/org/labkey/ms2/reader/PeptideProphetHandler.java @@ -15,7 +15,6 @@ */ package org.labkey.ms2.reader; -import org.labkey.api.reader.SimpleXMLStreamReader; import javax.xml.stream.XMLStreamException; diff --git a/ms2/src/org/labkey/ms2/reader/PeptideProphetSummary.java b/ms2/src/org/labkey/ms2/reader/PeptideProphetSummary.java index f2cb74630e..484c042095 100644 --- a/ms2/src/org/labkey/ms2/reader/PeptideProphetSummary.java +++ b/ms2/src/org/labkey/ms2/reader/PeptideProphetSummary.java @@ -22,8 +22,7 @@ import org.jetbrains.annotations.Nullable; import org.junit.Assert; import org.junit.Test; -import org.labkey.api.reader.SimpleXMLStreamReader; -import org.labkey.api.util.MatrixUtil; +import org.labkey.ms2.util.MatrixUtil; import javax.xml.stream.XMLStreamException; import java.nio.ByteBuffer; diff --git a/ms2/src/org/labkey/ms2/reader/ProtXmlReader.java b/ms2/src/org/labkey/ms2/reader/ProtXmlReader.java index a5fb648bd3..357bd94068 100644 --- a/ms2/src/org/labkey/ms2/reader/ProtXmlReader.java +++ b/ms2/src/org/labkey/ms2/reader/ProtXmlReader.java @@ -16,11 +16,10 @@ package org.labkey.ms2.reader; -import org.labkey.api.reader.SimpleXMLStreamReader; -import org.labkey.api.util.PossiblyGZIPpedFileInputStreamFactory; import org.labkey.ms2.IcatProteinQuantitation; import org.labkey.ms2.MS2Run; import org.labkey.ms2.pipeline.sequest.SequestRun; +import org.labkey.ms2.util.PossiblyGZIPpedFileInputStreamFactory; import org.labkey.vfs.FileLike; import javax.xml.stream.XMLStreamException; diff --git a/ms2/src/org/labkey/ms2/reader/ProteinGroup.java b/ms2/src/org/labkey/ms2/reader/ProteinGroup.java index 40f32a25f1..ebe3b5271d 100644 --- a/ms2/src/org/labkey/ms2/reader/ProteinGroup.java +++ b/ms2/src/org/labkey/ms2/reader/ProteinGroup.java @@ -15,7 +15,6 @@ */ package org.labkey.ms2.reader; -import org.labkey.api.reader.SimpleXMLStreamReader; import org.labkey.ms2.MS2Manager; import org.labkey.ms2.MS2Run; import org.labkey.ms2.reader.ProtXmlReader; diff --git a/ms2/src/org/labkey/ms2/reader/Q3AnalysisSummary.java b/ms2/src/org/labkey/ms2/reader/Q3AnalysisSummary.java index 0c47288ea1..50b68f14cd 100644 --- a/ms2/src/org/labkey/ms2/reader/Q3AnalysisSummary.java +++ b/ms2/src/org/labkey/ms2/reader/Q3AnalysisSummary.java @@ -15,7 +15,6 @@ */ package org.labkey.ms2.reader; -import org.labkey.api.reader.SimpleXMLStreamReader; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; diff --git a/ms2/src/org/labkey/ms2/reader/Q3Handler.java b/ms2/src/org/labkey/ms2/reader/Q3Handler.java index 5120ac0d45..8d706236fb 100644 --- a/ms2/src/org/labkey/ms2/reader/Q3Handler.java +++ b/ms2/src/org/labkey/ms2/reader/Q3Handler.java @@ -17,7 +17,6 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; -import org.labkey.api.reader.SimpleXMLStreamReader; import javax.xml.stream.XMLStreamException; import java.sql.PreparedStatement; diff --git a/ms2/src/org/labkey/ms2/reader/SequentialMzxmlIterator.java b/ms2/src/org/labkey/ms2/reader/SequentialMzxmlIterator.java index 85b62a09cc..e4c3aa610b 100644 --- a/ms2/src/org/labkey/ms2/reader/SequentialMzxmlIterator.java +++ b/ms2/src/org/labkey/ms2/reader/SequentialMzxmlIterator.java @@ -16,12 +16,11 @@ package org.labkey.ms2.reader; -import org.labkey.api.util.NetworkDrive; -import org.labkey.api.util.PossiblyGZIPpedFileInputStreamFactory; -import org.labkey.api.reader.SimpleXMLStreamReader; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.apache.xmlbeans.GDuration; +import org.labkey.api.util.NetworkDrive; +import org.labkey.ms2.util.PossiblyGZIPpedFileInputStreamFactory; import org.labkey.vfs.FileLike; import org.systemsbiology.jrap.Scan; diff --git a/ms2/src/org/labkey/ms2/reader/SimpleXMLStreamReader.java b/ms2/src/org/labkey/ms2/reader/SimpleXMLStreamReader.java new file mode 100644 index 0000000000..20767ccad5 --- /dev/null +++ b/ms2/src/org/labkey/ms2/reader/SimpleXMLStreamReader.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2008-2026 LabKey Corporation + * + * 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.labkey.ms2.reader; + +import org.labkey.api.util.XmlBeansUtil; + +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import java.io.InputStream; + +/** + * User: arauch + * Date: May 25, 2005 + * Time: 12:16:40 PM + */ +public class SimpleXMLStreamReader extends XMLStreamReaderWrapper +{ + public SimpleXMLStreamReader(InputStream stream) throws XMLStreamException + { + super(XmlBeansUtil.XML_INPUT_FACTORY.createXMLStreamReader(stream)); + } + + + public boolean skipToStart(String element) throws XMLStreamException + { + return skipTo(element, true); + } + + + public boolean skipToEnd(String element) throws XMLStreamException + { + return skipTo(element, false); + } + + + private boolean skipTo(String element, boolean start) throws XMLStreamException + { + while (hasNext()) + { + next(); + + if ((start ? isStartElement() : isEndElement()) && getLocalName().equals(element)) + return true; + } + + return false; + } + + + public String getHref() + throws XMLStreamException + { + while (hasNext()) + { + int event = next(); + + if (event == XMLStreamConstants.START_ELEMENT && "A".equals(getLocalName())) + return getAttributeValue("", "HREF"); + } + // UNDONE: Raise exception instead of returning null + return null; + } +} diff --git a/ms2/src/org/labkey/ms2/reader/TarIterator.java b/ms2/src/org/labkey/ms2/reader/TarIterator.java index 51ec81438a..76717a31e8 100644 --- a/ms2/src/org/labkey/ms2/reader/TarIterator.java +++ b/ms2/src/org/labkey/ms2/reader/TarIterator.java @@ -19,9 +19,9 @@ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.labkey.api.arrays.FloatArray; import org.labkey.ms2.FloatParser; import org.labkey.ms2.MS2Importer; +import org.labkey.ms2.arrays.FloatArray; import org.labkey.vfs.FileLike; import java.io.BufferedInputStream; diff --git a/ms2/src/org/labkey/ms2/reader/XMLStreamReaderWrapper.java b/ms2/src/org/labkey/ms2/reader/XMLStreamReaderWrapper.java new file mode 100644 index 0000000000..87ec34d807 --- /dev/null +++ b/ms2/src/org/labkey/ms2/reader/XMLStreamReaderWrapper.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2005-2026 Fred Hutchinson Cancer Research Center + * + * 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.labkey.ms2.reader; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +/** + * User: arauch + * Date: May 25, 2005 + * Time: 11:34:10 AM + */ +public class XMLStreamReaderWrapper implements XMLStreamReader +{ + private final XMLStreamReader streamReader; + + public XMLStreamReaderWrapper(XMLStreamReader streamReader) + { + this.streamReader = streamReader; + } + + @Override + public void close() throws XMLStreamException + { + streamReader.close(); + } + + @Override + public int getAttributeCount() + { + return streamReader.getAttributeCount(); + } + + @Override + public String getAttributeLocalName(int i) + { + return streamReader.getAttributeLocalName(i); + } + + @Override + public QName getAttributeName(int i) + { + return streamReader.getAttributeName(i); + } + + @Override + public String getAttributeNamespace(int i) + { + return streamReader.getAttributeNamespace(i); + } + + @Override + public String getAttributePrefix(int i) + { + return streamReader.getAttributePrefix(i); + } + + @Override + public String getAttributeType(int i) + { + return streamReader.getAttributeType(i); + } + + @Override + public String getAttributeValue(int i) + { + return streamReader.getAttributeValue(i); + } + + @Override + public String getAttributeValue(String s, String s1) + { + return streamReader.getAttributeValue(s, s1); + } + + @Override + public String getCharacterEncodingScheme() + { + return streamReader.getCharacterEncodingScheme(); + } + + @Override + public String getElementText() throws XMLStreamException + { + return streamReader.getElementText(); + } + + @Override + public String getEncoding() + { + return streamReader.getEncoding(); + } + + @Override + public int getEventType() + { + return streamReader.getEventType(); + } + + @Override + public String getLocalName() + { + return streamReader.getLocalName(); + } + + @Override + public Location getLocation() + { + return streamReader.getLocation(); + } + + @Override + public QName getName() + { + return streamReader.getName(); + } + + @Override + public NamespaceContext getNamespaceContext() + { + return streamReader.getNamespaceContext(); + } + + @Override + public int getNamespaceCount() + { + return streamReader.getNamespaceCount(); + } + + @Override + public String getNamespacePrefix(int i) + { + return streamReader.getNamespacePrefix(i); + } + + @Override + public String getNamespaceURI() + { + return streamReader.getNamespaceURI(); + } + + @Override + public String getNamespaceURI(int i) + { + return streamReader.getNamespaceURI(i); + } + + @Override + public String getNamespaceURI(String s) + { + return streamReader.getNamespaceURI(s); + } + + @Override + public String getPIData() + { + return streamReader.getPIData(); + } + + @Override + public String getPITarget() + { + return streamReader.getPITarget(); + } + + @Override + public String getPrefix() + { + return streamReader.getPrefix(); + } + + @Override + public Object getProperty(String s) throws IllegalArgumentException + { + return streamReader.getProperty(s); + } + + @Override + public String getText() + { + return streamReader.getText(); + } + + @Override + public char[] getTextCharacters() + { + return streamReader.getTextCharacters(); + } + + @Override + public int getTextCharacters(int i, char[] chars, int i1, int i2) throws XMLStreamException + { + return streamReader.getTextCharacters(i, chars, i1, i2); + } + + @Override + public int getTextLength() + { + return streamReader.getTextLength(); + } + + @Override + public int getTextStart() + { + return streamReader.getTextStart(); + } + + @Override + public String getVersion() + { + return streamReader.getVersion(); + } + + @Override + public boolean hasName() + { + return streamReader.hasName(); + } + + @Override + public boolean hasNext() throws XMLStreamException + { + return streamReader.hasNext(); + } + + @Override + public boolean hasText() + { + return streamReader.hasText(); + } + + @Override + public boolean isAttributeSpecified(int i) + { + return streamReader.isAttributeSpecified(i); + } + + @Override + public boolean isCharacters() + { + return streamReader.isCharacters(); + } + + @Override + public boolean isEndElement() + { + return streamReader.isEndElement(); + } + + @Override + public boolean isStandalone() + { + return streamReader.isStandalone(); + } + + @Override + public boolean isStartElement() + { + return streamReader.isStartElement(); + } + + @Override + public boolean isWhiteSpace() + { + return streamReader.isWhiteSpace(); + } + + @Override + public int next() throws XMLStreamException + { + return streamReader.next(); + } + + @Override + public int nextTag() throws XMLStreamException + { + return streamReader.nextTag(); + } + + @Override + public void require(int i, String s, String s1) throws XMLStreamException + { + streamReader.require(i, s, s1); + } + + @Override + public boolean standaloneSet() + { + return streamReader.standaloneSet(); + } +} diff --git a/ms2/src/org/labkey/ms2/reader/XPressAnalysisSummary.java b/ms2/src/org/labkey/ms2/reader/XPressAnalysisSummary.java index 615e9a7800..1ddbab400a 100644 --- a/ms2/src/org/labkey/ms2/reader/XPressAnalysisSummary.java +++ b/ms2/src/org/labkey/ms2/reader/XPressAnalysisSummary.java @@ -17,7 +17,6 @@ import net.systemsbiology.regisWeb.pepXML.XpressratioSummaryDocument; import org.apache.xmlbeans.XmlException; -import org.labkey.api.reader.SimpleXMLStreamReader; import javax.xml.stream.XMLStreamException; diff --git a/ms2/src/org/labkey/ms2/reader/XPressHandler.java b/ms2/src/org/labkey/ms2/reader/XPressHandler.java index cf655cf413..b3f7343cc4 100644 --- a/ms2/src/org/labkey/ms2/reader/XPressHandler.java +++ b/ms2/src/org/labkey/ms2/reader/XPressHandler.java @@ -15,7 +15,6 @@ */ package org.labkey.ms2.reader; -import org.labkey.api.reader.SimpleXMLStreamReader; import javax.xml.stream.XMLStreamException; import java.sql.PreparedStatement; diff --git a/ms2/src/org/labkey/ms2/reports/report/r/CustomRReport.java b/ms2/src/org/labkey/ms2/reports/report/r/CustomRReport.java new file mode 100644 index 0000000000..2af594d319 --- /dev/null +++ b/ms2/src/org/labkey/ms2/reports/report/r/CustomRReport.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2008-2026 LabKey Corporation + * + * 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.labkey.ms2.reports.report.r; + +import org.labkey.api.data.DataRegion; +import org.labkey.api.query.QueryView; +import org.labkey.api.reports.report.ReportDescriptor; +import org.labkey.api.reports.report.r.RReport; +import org.labkey.api.view.ActionURL; +import org.labkey.api.view.HttpView; +import org.labkey.api.view.ViewContext; + +/* +* User: Dave +* Date: Sep 5, 2008 +* Time: 1:31:51 PM +*/ +public abstract class CustomRReport extends RReport +{ + private String[] _forwardParams; + private String _type; + + protected CustomRReport(String[] forwardParams, String type) + { + _forwardParams = forwardParams; + _type = type; + } + + protected String[] getForwardParams() + { + return _forwardParams; + } + + protected abstract QueryView getQueryView(ViewContext context); + protected abstract boolean hasRequiredParams(ViewContext context); + + @Override + public String getType() + { + return _type; + } + + @Override + public ActionURL getEditReportURL(ViewContext context) + { + return null; //no editing from manage page + } + + @Override + protected QueryView createQueryView(ViewContext context, ReportDescriptor descriptor) + { + QueryView view = getQueryView(context); + + //need to apply view name if defined in the descriptor + //the URL to run the report does not contain the viewName parameter in the query string + //so QuerySettings.init() won't automatically apply it + String customViewName = descriptor.getProperty(ReportDescriptor.Prop.viewName); + if(null != customViewName) + view.setCustomView(customViewName); + + return view; + } + + @Override + public ActionURL getDownloadDataURL(ViewContext context) + { + ActionURL url = super.getDownloadDataURL(context); + return hasRequiredParams(context) ? addForwardParams(url, context) : null; + } + + @Override + public HttpView renderDataView(ViewContext context) throws Exception + { + QueryView view = createQueryView(context, getDescriptor()); + view.getSettings().setReportId(null); + view.setButtonBarPosition(DataRegion.ButtonBarPosition.NONE); + return view; + } + + @Override + public ActionURL getRunReportURL(ViewContext context) + { + ActionURL url = super.getRunReportURL(context); + return hasRequiredParams(context) ? addForwardParams(url, context) : null; + } + + + protected ActionURL addForwardParams(ActionURL url, ViewContext context) + { + for(String name : getForwardParams()) + { + String value = context.getActionURL().getParameter(name); + if(null != value) + url.replaceParameter(name, value); + } + return url; + } +} diff --git a/ms2/src/org/labkey/ms2/search/AbstractXMLDocumentParser.java b/ms2/src/org/labkey/ms2/search/AbstractXMLDocumentParser.java new file mode 100644 index 0000000000..d6e98e1d45 --- /dev/null +++ b/ms2/src/org/labkey/ms2/search/AbstractXMLDocumentParser.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2010-2026 LabKey Corporation + * + * 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.labkey.ms2.search; + +import org.labkey.api.search.AbstractDocumentParser; +import org.labkey.api.util.XmlBeansUtil; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; + +/** + * Base class that parses XML files and passes a subset of their content on to Lucene for indexing + * User: jeckels + * Date: Jul 15, 2010 + */ +public abstract class AbstractXMLDocumentParser extends AbstractDocumentParser +{ + + /** Fires off a SAX parse that doesn't do schema validation */ + @Override + public void parseContent(InputStream stream, ContentHandler handler) throws IOException, SAXException + { + try (BufferedInputStream buffered = new BufferedInputStream(stream)) + { + XmlBeansUtil.SAX_PARSER_FACTORY.newSAXParser().parse(buffered, createSAXHandler(handler)); + } + catch (ParserConfigurationException e) + { + throw new SAXException(e); + } + } + + /** Creates a SAX callback handler that passes the appropriate subset of data on to Lucene */ + protected abstract DefaultHandler createSAXHandler(ContentHandler xhtmlHandler); + + public static class SAXHandler extends DefaultHandler + { + /** Suggested default is 1 MB */ + public static final long DEFAULT_MAX_INDEXABLE_SIZE = 1024 * 1024; + + private final ContentHandler _xhtmlHandler; + private final boolean _includeElementNames; + private final boolean _includeAttributeNames; + private final boolean _includeAttributeValues; + private final boolean _includeText; + private final long _maxIndexableSize; + + private final Set _stopElements = new HashSet<>(); + + /** How many characters have been included in document to be indexed so far */ + private long _indexedSize = 0; + + /** + * @param handler Lucene listener for content + * @param includeElementNames whether to include the names of XML elements in the index + * @param includeAttributeNames whether to include the names of XML attributes in the index + * @param includeAttributeValues whether to include the values of XML attributes in the index + * @param includeText whether to XML character data in the index + * @param maxIndexableSize maximum number of characters to add to index. -1 to have no limit. Parsing + * stops once the max has been reached. + */ + public SAXHandler(ContentHandler handler, boolean includeElementNames, boolean includeAttributeNames, + boolean includeAttributeValues, boolean includeText, long maxIndexableSize) + { + _xhtmlHandler = handler; + _includeElementNames = includeElementNames; + _includeAttributeNames = includeAttributeNames; + _includeAttributeValues = includeAttributeValues; + _includeText = includeText; + _maxIndexableSize = maxIndexableSize; + } + + /** Element name at which the parsing should stop completely */ + public void addStopElement(String elementName) + { + _stopElements.add(elementName); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException + { + if (_stopElements.contains(localName) || _stopElements.contains(qName)) + { + throw new ParseFinishedException(); + } + + StringBuilder sb = new StringBuilder(); + if (_includeElementNames) + { + sb.append(localName); + sb.append(" "); + } + for (int i = 0; i < attributes.getLength(); i++) + { + String attributeName = attributes.getLocalName(i); + String attributeQName = attributes.getQName(i); + // Don't include some of the XML overhead + if (!attributeQName.equals("xmlns") && !attributeQName.startsWith("xmlns:") && !attributeQName.equals("xsi:schemaLocation")) + { + if (_includeAttributeNames) + { + sb.append(attributeName); + sb.append(" "); + } + if (_includeAttributeValues) + { + sb.append(attributes.getValue(i)); + sb.append(" "); + } + } + } + sb.append("\n"); + _xhtmlHandler.characters(sb.toString().toCharArray(), 0, sb.length()); + + contentAdded(sb.length()); + } + + private void contentAdded(int length) throws ParseFinishedException + { + _indexedSize += length; + if (_maxIndexableSize >= 0 && _indexedSize > _maxIndexableSize) + { + throw new ParseFinishedException(); + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException + { + if (_includeText) + { + _xhtmlHandler.characters(ch, start, length); + contentAdded(length); + } + } + } +} diff --git a/ms2/src/org/labkey/ms2/settings/PreferenceService.java b/ms2/src/org/labkey/ms2/settings/PreferenceService.java new file mode 100644 index 0000000000..c1364d5fd0 --- /dev/null +++ b/ms2/src/org/labkey/ms2/settings/PreferenceService.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2008-2026 LabKey Corporation + * + * 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.labkey.ms2.settings; + +import jakarta.servlet.http.HttpSession; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerManager; +import org.labkey.api.data.PropertyManager; +import org.labkey.api.data.PropertyManager.WritablePropertyMap; +import org.labkey.api.security.User; +import org.labkey.api.view.HttpView; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * User: Karl Lum + * Date: Jan 19, 2007 + */ +public class PreferenceService +{ + private static final String PREFERENCE_SERVICE_MAP_KEY = "PreferenceServiceMap"; + private static final PreferenceService _instance = new PreferenceService(); + + private final Map> _nullPreferenceMap = Collections.synchronizedMap(new HashMap<>()); + + public static PreferenceService get() + { + return _instance; + } + + private PreferenceService() + { + } + + public Object getProperty(String name, Container container) + { + return getPreferences(container).get(name); + } + + /** Uses a session-based set of properties if the user is a guest, and a database-backed set if authenticated */ + public String getProperty(String name, User user) + { + if (user == null || user.isGuest()) + return getSessionPreferences().get(name); + + return getPreferences(user).get(name); + } + + /** + * Returns the container that the specified property is set on. Will return the + * first container matching the property name in the inheritance chain, from + * most specific to least specific. + * + * @param initialScope - the container of interest (to start the search from) + */ + public Container findContainerFor(String name, Container initialScope) + { + if (initialScope == null) + throw new IllegalArgumentException("Initial container scope cannot be null"); + + if(initialScope.isRoot()) + return initialScope; + + Container c = initialScope; + while (c != null) + { + Map p = getReadOnlyPreferences(c); + if (p != null) + { + if (p.containsKey(name)) + return c; + } + if (c.isRoot()) + break; + c = c.getParent(); + } + return null; + } + + public boolean isInherited(String name, Container container) + { + if (container == null || container.isRoot()) + return false; + + Container owningContainer = findContainerFor(name, container); + return (owningContainer != null && !container.equals(owningContainer)); + } + + public void setProperty(String name, String value, Container container) + { + WritablePropertyMap prefs = getWritablePreferences(container); + prefs.put(name, value); + + prefs.save(); + _nullPreferenceMap.remove(container.getId()); + } + + /** Uses a session-based set of properties if the user is a guest, and a database-backed set if authenticated */ + public void setProperty(String name, String value, User user) + { + if (user.isGuest()) + { + Map prefs = getSessionPreferences(); + prefs.put(name, value); + } + else + { + WritablePropertyMap prefs = getWritablePreferences(user); + prefs.put(name, value); + prefs.save(); + } + } + + public void deleteProperty(String name, Container container) + { + WritablePropertyMap prefs = getWritablePreferences(container); + prefs.remove(name); + + prefs.save(); + _nullPreferenceMap.remove(container.getId()); + } + + public void deleteProperty(String name, User user) + { + if (user.isGuest()) + { + Map prefs = getSessionPreferences(); + prefs.remove(name); + } + else + { + WritablePropertyMap prefs = getWritablePreferences(user); + prefs.remove(name); + prefs.save(); + } + } + + private Map getPreferences(Container container) + { + Stack stack = new Stack<>(); + stack.push(container); + while (!container.isRoot()) + { + stack.push(container.getParent()); + container = container.getParent(); + } + + Map prefs = new HashMap<>(); + while (!stack.isEmpty()) + { + Container c = stack.pop(); + Map p = getReadOnlyPreferences(c); + if (p != null) + { + prefs.putAll(p); + } + } + return Collections.unmodifiableMap(prefs); + } + + private Map getSessionPreferences() + { + HttpSession session = HttpView.currentRequest().getSession(true); + Map prefs = (Map) session.getAttribute(PREFERENCE_SERVICE_MAP_KEY); + + if (null == prefs) + { + prefs = new HashMap<>(); + session.setAttribute(PREFERENCE_SERVICE_MAP_KEY, prefs); + } + + return prefs; + } + + private PropertyManager.PropertyMap getPreferences(User user) + { + return PropertyManager.getProperties(user, ContainerManager.getRoot(), PREFERENCE_SERVICE_MAP_KEY); + } + + private WritablePropertyMap getWritablePreferences(Container container) + { + return PropertyManager.getWritableProperties(container, PREFERENCE_SERVICE_MAP_KEY, true); + } + + private WritablePropertyMap getWritablePreferences(User user) + { + return PropertyManager.getWritableProperties(user, ContainerManager.getRoot(), PREFERENCE_SERVICE_MAP_KEY, true); + } + + private PropertyManager.PropertyMap getReadOnlyPreferences(Container container) + { + String containerId = container.getId(); + if (_nullPreferenceMap.containsKey(containerId)) + return null; + PropertyManager.PropertyMap prefs = PropertyManager.getProperties(container, PREFERENCE_SERVICE_MAP_KEY); + if (prefs.isEmpty()) + { + _nullPreferenceMap.put(containerId, null); + } + return prefs; + } +} diff --git a/ms2/src/org/labkey/ms2/util/MatrixUtil.java b/ms2/src/org/labkey/ms2/util/MatrixUtil.java new file mode 100644 index 0000000000..5d552309cc --- /dev/null +++ b/ms2/src/org/labkey/ms2/util/MatrixUtil.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2004-2016 Fred Hutchinson Cancer Research Center + * + * 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.labkey.ms2.util; + +import Jama.Matrix; + +public class MatrixUtil +{ + // Takes Matrix X and Matrix Y and returns Matrix containing b values + public static Matrix linearRegression(Matrix X, Matrix Y) + { + Matrix XTrans = X.transpose(); + + return XTrans.times(X).inverse().times(XTrans).times(Y); + } + + + // Java-friendly version that takes an array of arrays of x values, and an array of y values + // Returns array of b values + public static double[] linearRegression(double[][] xArray, double[] yArray) + { + int pointCount = yArray.length; + int independentCount = xArray.length; + double[][] x = new double[pointCount][independentCount + 1]; + double[][] y = new double[pointCount][1]; + + // Transpose into new arrays and add column of 1s to x + for (int i = 0; i < pointCount; i++) + { + y[i][0] = yArray[i]; + x[i][0] = 1; + + for (int j = 0; j < independentCount; j++) + x[i][j + 1] = xArray[j][i]; + } + + return linearRegression(new Matrix(x), new Matrix(y)).getRowPackedCopy(); + } + + + // Simple version for single independent variable: y = a + bx + public static double[] linearRegression(double[] xArray, double[] yArray) + { + return linearRegression(new double[][]{xArray}, yArray); + } + + + // Takes Matrix X and Matrix Y and returns Matrix containing R-squared + public static Matrix r2(Matrix X, Matrix Y) + { + Matrix XTrans = X.transpose(); + Matrix YTrans = Y.transpose(); + + return YTrans.times(X).times( + XTrans.times(X).inverse() + ).times(XTrans).times(Y).times( + YTrans.times(Y).inverse()); + } + + + public static double r2(double[][] xArray, double[] yArray) + { + int pointCount = yArray.length; + int independentCount = xArray.length; + + double[][] x = new double[pointCount][independentCount]; + double[][] y = new double[pointCount][1]; + double[] xTotal = new double[independentCount]; + double[] xAvg = new double[independentCount]; + double yTotal = 0; + + // Calculate average of x columns, y + for (int i = 0; i < pointCount; i++) + { + yTotal += yArray[i]; + + for (int j = 0; j < independentCount; j++) + xTotal[j] += xArray[j][i]; + } + + for (int i = 0; i < independentCount; i++) + xAvg[i] = xTotal[i] / pointCount; + + double yAvg = yTotal / pointCount; + + // Create deviation score form, transpose into new arrays + for (int i = 0; i < pointCount; i++) + { + y[i][0] = yArray[i] - yAvg; + + for (int j = 0; j < independentCount; j++) + x[i][j] = xArray[j][i] - xAvg[j]; + } + + return r2(new Matrix(x), new Matrix(y)).get(0, 0); + } + + + // Simple version for single independent variable: y = a + bx + public static double r2(double[] xArray, double[] yArray) + { + return r2(new double[][]{xArray}, yArray); + } + + + public static double r2(float[] xArray, float[] yArray) + { + return r2(toDoubleArray(xArray), toDoubleArray(yArray)); + } + + + private static double[] toDoubleArray(float[] array) + { + double[] dArray = new double[array.length]; + + for (int i = 0; i < array.length; i++) + dArray[i] = array[i]; + + return dArray; + } + + + public static double sigma(double[] x, double[] y, double[] b) + { + double errorTotal = 0; + + for (int i = 0; i < y.length; i++) + { + errorTotal += Math.pow(y[i] - (b[0] + b[1] * x[i]), 2); + } + + return Math.sqrt(errorTotal / (y.length - 2)); + } +} diff --git a/ms2/src/org/labkey/ms2/util/PossiblyGZIPpedFileInputStreamFactory.java b/ms2/src/org/labkey/ms2/util/PossiblyGZIPpedFileInputStreamFactory.java new file mode 100644 index 0000000000..402a0d0941 --- /dev/null +++ b/ms2/src/org/labkey/ms2/util/PossiblyGZIPpedFileInputStreamFactory.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010-2026 LabKey Corporation + * + * 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.labkey.ms2.util; + +import org.labkey.vfs.FileLike; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.GZIPInputStream; + + +/** + * examine a file and access as a gzip file if appropriate + * + * note this would be tidier as "public class PossiblyGZIPpedFileInputStream extends InputStream" but + * I worry about performance since the read() call gets hit a lot and would rather not insert + * another deeply loop nested function call PossiblyGZIPpedFileInputStream.read->_iStream.read() + * + * bpratt, Insilicos + * + */ +abstract public class PossiblyGZIPpedFileInputStreamFactory +{ + private static final int STREAM_BUFFER_SIZE = 128 * 1024; + + static public InputStream getStream(FileLike f) throws IOException + { + InputStream fis = f.openInputStream(); + try + { + return new GZIPInputStream(fis, STREAM_BUFFER_SIZE); + } + catch (java.io.IOException e) + { + // not a gzip file - reopen since we ate a couple of bytes + try + { + fis.close(); + } + catch (java.io.IOException ee) + { + // seems unlikely at this point + } + return f.openInputStream(); + } + } +} diff --git a/nab/src/org/labkey/nab/query/NabRunDataTable.java b/nab/src/org/labkey/nab/query/NabRunDataTable.java index 59ac596d6f..2e58655f7f 100644 --- a/nab/src/org/labkey/nab/query/NabRunDataTable.java +++ b/nab/src/org/labkey/nab/query/NabRunDataTable.java @@ -43,7 +43,6 @@ import org.labkey.api.query.FieldKey; import org.labkey.api.query.LookupForeignKey; import org.labkey.api.query.PropertyForeignKey; -import org.labkey.api.query.QcAwarePropertyForeignKey; import org.labkey.api.assay.AbstractAssayProvider; import org.labkey.api.assay.AssayProtocolSchema; import org.labkey.api.assay.AssayProvider; diff --git a/nab/src/org/labkey/nab/query/QcAwarePropertyForeignKey.java b/nab/src/org/labkey/nab/query/QcAwarePropertyForeignKey.java new file mode 100644 index 0000000000..aecc035e25 --- /dev/null +++ b/nab/src/org/labkey/nab/query/QcAwarePropertyForeignKey.java @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2009-2026 LabKey Corporation + * + * 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.labkey.nab.query; + +import org.labkey.api.data.BaseColumnInfo; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.DisplayColumn; +import org.labkey.api.data.DisplayColumnFactory; +import org.labkey.api.data.JdbcType; +import org.labkey.api.data.MVDisplayColumnFactory; +import org.labkey.api.data.OORDisplayColumnFactory; +import org.labkey.api.data.OutOfRangeDisplayColumn; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.MvColumn; +import org.labkey.api.exp.PropertyColumn; +import org.labkey.api.exp.PropertyDescriptor; +import org.labkey.api.exp.PropertyType; +import org.labkey.api.exp.RawValueColumn; +import org.labkey.api.query.ExprColumn; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.PropertyForeignKey; +import org.labkey.api.query.QuerySchema; +import org.labkey.api.query.QueryService; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * User: brittp + * Date: Oct 25, 2007 + * Time: 10:06:18 AM + */ +public class QcAwarePropertyForeignKey extends PropertyForeignKey +{ + private final TableInfo _baseTable; + private final QcMetadata _metadata; + + public QcAwarePropertyForeignKey(QuerySchema schema, TableInfo baseTable, Collection pds) + { + this(getDisplayPds(pds), baseTable, schema); + } + + private QcAwarePropertyForeignKey(QcMetadata metadata, TableInfo baseTable, QuerySchema schema) + { + super(schema, null, metadata.getDisplayProperties()); + _baseTable = baseTable; + _metadata = metadata; + } + + + private static QcMetadata getDisplayPds(Collection pds) + { + final Map nameToPropertyMap = new HashMap<>(); + for (PropertyDescriptor pd : pds) + nameToPropertyMap.put(pd.getName(), pd); + QcMetadata metadata = new QcMetadata(); + + for (PropertyDescriptor displayPd : pds) + { + PropertyDescriptor oorIndicatorPd = nameToPropertyMap.get(displayPd.getName() + OORDisplayColumnFactory.OOR_INDICATOR_COLUMN_SUFFIX); + if (oorIndicatorPd != null) + { + metadata.addOORPropertyDescriptor(displayPd, oorIndicatorPd); + } + else if (!(displayPd.getName().toLowerCase().endsWith(OORDisplayColumnFactory.OOR_INDICATOR_COLUMN_SUFFIX.toLowerCase()) && + nameToPropertyMap.containsKey(displayPd.getName().substring(0, displayPd.getName().length() - OORDisplayColumnFactory.OOR_INDICATOR_COLUMN_SUFFIX.length())))) + { + metadata.addNonOORPropertyDescriptor(displayPd); + } + } + return metadata; + } + + @Override + protected BaseColumnInfo constructColumnInfo(ColumnInfo parent, FieldKey name, PropertyDescriptor pd) + { + OORColumnGroup group = _metadata.getOORGroupByDisplayColumn(pd); + if (group != null) + { + BaseColumnInfo col = super.constructColumnInfo(parent, name, pd); + col.setDisplayColumnFactory(new LateBoundOORDisplayColumnFactory(group.getBaseName())); + return col; + } + + group = _metadata.getOORGroupByInRangeColumn(pd); + if (group != null) + { + BaseColumnInfo inRangeColumn = new InRangeExprColumn(_baseTable, name, group.getBaseName()); + inRangeColumn.setLabel(group.getInRangePd().getLabel()); + inRangeColumn.setFormat(group.getNumberPd().getFormat()); + return inRangeColumn; + } + + if (pd.isMvEnabled()) + { + // Just need to set the display column factory + BaseColumnInfo col = super.constructColumnInfo(parent, name, pd); + col.setMvColumnName(new FieldKey(null, pd.getName() + MvColumn.MV_INDICATOR_SUFFIX)); + col.setDisplayColumnFactory(new MVDisplayColumnFactory()); + return col; + } + + QcColumnGroup qcGroup = _metadata.getQcColumnGroupByIndicator(pd); + if (qcGroup != null) + { + if (parent == null) + { + return new BaseColumnInfo(pd.getName(), pd.getJdbcType()); + } + PropertyColumn qcColumn = new PropertyColumn(pd, parent, _schema.getContainer(), _schema.getUser(), false); + qcColumn.setParentIsObjectId(_parentIsObjectId); + qcColumn.setMvIndicatorColumn(true); + qcColumn.setNullable(true); + qcColumn.setUserEditable(false); + return qcColumn; + } + + return super.constructColumnInfo(parent, name, pd); + } + + public Collection getDefaultHiddenProperties() + { + return _metadata.getDefaultHiddenProperties(); + } + + private static ColumnInfo getIndicatorColumn(String baseName, ColumnInfo colInfo) + { + return getNamedColumn(baseName, colInfo, OORDisplayColumnFactory.OOR_INDICATOR_COLUMN_SUFFIX); + } + + private static ColumnInfo getNamedColumn(String baseName, ColumnInfo colInfo, String suffix) + { + FieldKey thisFieldKey = FieldKey.fromString(colInfo.getName()); + List keys = new ArrayList<>(); + + FieldKey otherKey = new FieldKey(thisFieldKey.getParent(), baseName + suffix); + keys.add(otherKey); + Map cols = QueryService.get().getColumns(colInfo.getParentTable(), keys); + return cols.get(otherKey); + } + + private static class LateBoundOORDisplayColumnFactory implements DisplayColumnFactory + { + private final String _baseName; + + public LateBoundOORDisplayColumnFactory(String baseName) + { + _baseName = baseName; + } + + @Override + public DisplayColumn createRenderer(ColumnInfo colInfo) + { + ColumnInfo oorIndicator = getIndicatorColumn(_baseName, colInfo); + return new OutOfRangeDisplayColumn(colInfo, oorIndicator); + } + } + + private static class InRangeExprColumn extends ExprColumn + { + private final String _baseName; + + private InRangeExprColumn(TableInfo parent, FieldKey name, String baseName) + { + super(parent, name, null, JdbcType.INTEGER); + _baseName = baseName; + } + + @Override + public SQLFragment getValueSql(String tableAlias) + { + ColumnInfo indicatorCol = getIndicatorColumn(_baseName, this); + ColumnInfo numberCol = getNumberColumn(_baseName, this); + + SQLFragment inRangeSQL = new SQLFragment("CASE WHEN ("); + inRangeSQL.append(indicatorCol.getValueSql(tableAlias)); + inRangeSQL.unsafeAppend(") IS NULL THEN ("); + inRangeSQL.append(numberCol.getValueSql(tableAlias)); + inRangeSQL.unsafeAppend(") ELSE NULL END"); + return inRangeSQL; + } + + @Override + public void declareJoins(String parentAlias, Map map) + { + ColumnInfo indicatorCol = getIndicatorColumn(_baseName, this); + if (indicatorCol == null) + { + throw new IllegalStateException("Could not find " + _baseName + OORDisplayColumnFactory.OOR_INDICATOR_COLUMN_SUFFIX + " column"); + } + indicatorCol.declareJoins(parentAlias, map); + ColumnInfo numberCol = getNumberColumn(_baseName, this); + if (numberCol == null) + { + throw new IllegalStateException("Could not find " + _baseName + OORDisplayColumnFactory.NUMBER_COLUMN_SUFFIX + " column"); + } + numberCol.declareJoins(parentAlias, map); + } + + private ColumnInfo getNumberColumn(String baseName, ColumnInfo colInfo) + { + return getNamedColumn(baseName, colInfo, OORDisplayColumnFactory.NUMBER_COLUMN_SUFFIX); + } + } + + private static class OORColumnGroup + { + private final String _baseName; + private final PropertyDescriptor _displayPd; + private final PropertyDescriptor _indicatorPd; + private final PropertyDescriptor _numberPd; + private final PropertyDescriptor _inRangePd; + + private OORColumnGroup(PropertyDescriptor displayPd, PropertyDescriptor indicatorPd, + PropertyDescriptor numberPd, PropertyDescriptor inRangePd) + { + _baseName = displayPd.getName(); + _displayPd = displayPd; + _indicatorPd = indicatorPd; + _numberPd = numberPd; + _inRangePd = inRangePd; + } + + private PropertyDescriptor getDisplayPd() + { + return _displayPd; + } + + private PropertyDescriptor getIndicatorPd() + { + return _indicatorPd; + } + + private PropertyDescriptor getNumberPd() + { + return _numberPd; + } + + private PropertyDescriptor getInRangePd() + { + return _inRangePd; + } + + private String getBaseName() + { + return _baseName; + } + } + + private static class QcColumnGroup + { + private final String _baseName; + private final PropertyDescriptor _displayPd; + private final PropertyDescriptor _indicatorPd; + private final PropertyDescriptor _rawValuePd; + + private QcColumnGroup(PropertyDescriptor displayPd, PropertyDescriptor indicatorPd, PropertyDescriptor rawValuePd) + { + _baseName = displayPd.getName(); + _displayPd = displayPd; + _indicatorPd = indicatorPd; + _rawValuePd = rawValuePd; + } + + private PropertyDescriptor getDisplayPd() + { + return _displayPd; + } + + private PropertyDescriptor getIndicatorPd() + { + return _indicatorPd; + } + + private PropertyDescriptor getRawValuePd() + { + return _rawValuePd; + } + + private String getBaseName() + { + return _baseName; + } + } + + private static class QcMetadata + { + private final List _oorGroups = new ArrayList<>(); + private final List _qcGroups = new ArrayList<>(); + private final List _additionalDisplayPds = new ArrayList<>(); + + private void addOORPropertyDescriptor(PropertyDescriptor displayPd, PropertyDescriptor indicatorPd) + { + PropertyDescriptor numberPd = displayPd.clone(); + numberPd.setName(displayPd.getName() + OORDisplayColumnFactory.NUMBER_COLUMN_SUFFIX); + numberPd.setLabel(displayPd.getName() + " Number"); + + PropertyDescriptor inRangePd = displayPd.clone(); + inRangePd.setName(displayPd.getName() + OORDisplayColumnFactory.IN_RANGE_COLUMN_SUFFIX); + inRangePd.setLabel(displayPd.getName() + " In Range"); + _oorGroups.add(new OORColumnGroup(displayPd, indicatorPd, numberPd, inRangePd)); + } + + private void addNonOORPropertyDescriptor(PropertyDescriptor pd) + { + // May be a MV PD, but not OOR specifically + if (pd.isMvEnabled()) + { + PropertyDescriptor indicatorPd = pd.clone(); + indicatorPd.setName(pd.getName() + MvColumn.MV_INDICATOR_SUFFIX); + indicatorPd.setLabel(pd.getName() + " MV Indicator"); + indicatorPd.setRangeURI(PropertyType.STRING.getTypeUri()); + indicatorPd.setMvEnabled(false); + + PropertyDescriptor rawValuePd = pd.clone(); + rawValuePd.setName(pd.getName() + RawValueColumn.RAW_VALUE_SUFFIX); + rawValuePd.setLabel(pd.getName() + " Raw Value"); + rawValuePd.setMvEnabled(false); + + _qcGroups.add(new QcColumnGroup(pd, indicatorPd, rawValuePd)); + } + else + { + _additionalDisplayPds.add(pd); + } + } + + private List getDisplayProperties() + { + List pds = new ArrayList<>(_additionalDisplayPds); + for (OORColumnGroup group : _oorGroups) + { + pds.add(group.getDisplayPd()); + pds.add(group.getIndicatorPd()); + pds.add(group.getInRangePd()); + pds.add(group.getNumberPd()); + } + for (QcColumnGroup group : _qcGroups) + { + pds.add(group.getDisplayPd()); + pds.add(group.getIndicatorPd()); + pds.add(group.getRawValuePd()); + } + return pds; + } + + private Collection getDefaultHiddenProperties() + { + List pds = new ArrayList<>(); + for (OORColumnGroup group : _oorGroups) + { + pds.add(group.getIndicatorPd()); + pds.add(group.getInRangePd()); + pds.add(group.getNumberPd()); + } + for (QcColumnGroup group : _qcGroups) + { + pds.add(group.getIndicatorPd()); + pds.add(group.getRawValuePd()); + } + return pds; + } + + private OORColumnGroup getOORGroupByDisplayColumn(PropertyDescriptor pd) + { + for (OORColumnGroup group : _oorGroups) + { + if (pd.getName().equals(group.getDisplayPd().getName())) + return group; + } + return null; + } + + private OORColumnGroup getOORGroupByInRangeColumn(PropertyDescriptor pd) + { + for (OORColumnGroup group : _oorGroups) + { + if (pd.getName().equals(group.getInRangePd().getName())) + return group; + } + return null; + } + + private QcColumnGroup getQcColumnGroup(PropertyDescriptor pd) + { + for (QcColumnGroup group : _qcGroups) + { + if (pd.getName().equals(group.getDisplayPd().getName())) + return group; + } + return null; + } + + private QcColumnGroup getQcColumnGroupByIndicator(PropertyDescriptor pd) + { + for (QcColumnGroup group : _qcGroups) + { + if (pd.getName().equals(group.getIndicatorPd().getName())) + return group; + } + return null; + } + } +} diff --git a/protein/src/org/labkey/protein/view/proteinCoverageMap.jsp b/protein/src/org/labkey/protein/view/proteinCoverageMap.jsp index 6aca498381..c21f40baac 100644 --- a/protein/src/org/labkey/protein/view/proteinCoverageMap.jsp +++ b/protein/src/org/labkey/protein/view/proteinCoverageMap.jsp @@ -23,7 +23,7 @@ <%@ page import="org.labkey.api.view.HttpView" %> <%@ page import="org.labkey.api.view.JspView" %> <%@ page import="org.labkey.api.view.template.ClientDependencies" %> -<%@ page import="org.labkey.api.visualization.ColorGradient" %> +<%@ page import="org.labkey.protein.visualization.ColorGradient" %> <%@ page import="java.awt.*" %> <%@ page import="java.util.ArrayList" %> <%@ page import="java.util.Collections" %> diff --git a/protein/src/org/labkey/protein/visualization/ColorGradient.java b/protein/src/org/labkey/protein/visualization/ColorGradient.java new file mode 100644 index 0000000000..ebaa06e8a1 --- /dev/null +++ b/protein/src/org/labkey/protein/visualization/ColorGradient.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022-2026 LabKey Corporation + * + * 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.labkey.protein.visualization; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +public class ColorGradient +{ + + /** + * inspired from https://github.com/matthewbeckler/HeatMap/blob/master/Gradient.java + * + * Creates an array of Color objects for use as a gradient, using a linear + * interpolation between the two specified colors. + * + * @param one Color used for the bottom of the gradient + * @param two Color used for the top of the gradient + * @param numSteps The number of steps in the gradient. 250 is a good number. + */ + public static List createGradient(final Color one, final Color two, final int numSteps) + { + int r1 = one.getRed(); + int g1 = one.getGreen(); + int b1 = one.getBlue(); + int a1 = one.getAlpha(); + + int r2 = two.getRed(); + int g2 = two.getGreen(); + int b2 = two.getBlue(); + int a2 = two.getAlpha(); + + int newR; + int newG; + int newB; + int newA; + + List gradient = new ArrayList<>(); + double iNorm; + for (int i = 0; i < numSteps; i++) + { + iNorm = i / (double) numSteps; //a normalized [0:1] variable + newR = (int) (r1 + iNorm * (r2 - r1)); + newG = (int) (g1 + iNorm * (g2 - g1)); + newB = (int) (b1 + iNorm * (b2 - b1)); + newA = (int) (a1 + iNorm * (a2 - a1)); + gradient.add(new Color(newR, newG, newB, newA)); + } + + return gradient; + } + + + public static float calculateLuminance(Color color) + { + return (float) (0.2126 * color.getRed() + 0.7152 * color.getGreen() + 0.0722 * color.getBlue()); + } + public static String getContrastingForegroundColor(Color color) + { + float luminance = calculateLuminance(color); + return (luminance < 140) ? "#fff" : "#000"; + } +} \ No newline at end of file