Repository: aidenlab/Juicebox
Branch: master
Commit: 9697464526f6
Files: 472
Total size: 91.9 MB
Directory structure:
gitextract_y7dv046v/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ ├── custom.md
│ └── feature_request.md
├── .gitignore
├── .idea/
│ ├── artifacts/
│ │ ├── Juicebox.xml
│ │ └── JuicerTools.xml
│ ├── compiler.xml
│ ├── copyright/
│ │ ├── Juicebox.xml
│ │ └── profiles_settings.xml
│ ├── encodings.xml
│ ├── jarRepositories.xml
│ ├── kotlinc.xml
│ ├── libraries/
│ │ ├── KotlinJavaRuntime.xml
│ │ ├── broadinstitute.xml
│ │ ├── general.xml
│ │ ├── jargs.xml
│ │ ├── jcuda.xml
│ │ ├── jfreechart_1_0_19.xml
│ │ ├── log4j_core_2_11_0.xml
│ │ └── npy.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── scopes/
│ │ ├── Juicebox_Source.xml
│ │ └── scope_settings.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── HiCFormatV8.md
├── HiC_format_v7.docx
├── JuiceboxIcon.icns
├── Juicer.README
├── LICENSE
├── README.md
├── benchmark.sh
├── build.xml
├── data/
│ └── inter.hic
├── internalREADME.md
├── juicebox.properties
├── l4j/
│ ├── config.xml
│ ├── config_bcm.xml
│ ├── config_broad.xml
│ ├── config_fix_for_Erez.xml
│ └── config_nosplash.xml
├── lib/
│ ├── appbundler-1.0.jar
│ ├── broadinstitute/
│ │ ├── htsjdk-1.139.jar
│ │ ├── igv.jar
│ │ └── log4j-core-2.11.0.jar
│ ├── general/
│ │ ├── VectorGraphics2D-0.11.jar
│ │ ├── VectorGraphics2D.LGPL.LICENSE
│ │ ├── commons-math3-3.6.1.jar
│ │ ├── guava-18.0.jar
│ │ ├── hic_tools.3.30.00.jar
│ │ ├── jahmm-0.6.1.jar
│ │ ├── jargs.jar
│ │ ├── jfreechart-1.0.19.jar
│ │ ├── jsi1.1.jar
│ │ └── npy.jar
│ ├── jarbundler-core-3.3.0.jar
│ ├── jcuda/
│ │ ├── jcuda-0.7.0.jar
│ │ ├── jcuda-0.8.0.jar
│ │ ├── jcuda-natives-0.8.0-apple-x86_64.jar
│ │ ├── jcuda-natives-0.8.0-linux-ppc_64.jar
│ │ ├── jcuda-natives-0.8.0-linux-x86_64.jar
│ │ └── jcudaUtils-0.0.4.jar
│ ├── kotlin/
│ │ ├── kotlin-reflect-sources.jar
│ │ ├── kotlin-reflect.jar
│ │ ├── kotlin-stdlib-jdk7-sources.jar
│ │ ├── kotlin-stdlib-jdk7.jar
│ │ ├── kotlin-stdlib-jdk8-sources.jar
│ │ ├── kotlin-stdlib-jdk8.jar
│ │ ├── kotlin-stdlib-sources.jar
│ │ ├── kotlin-stdlib.jar
│ │ ├── kotlin-test-sources.jar
│ │ └── kotlin-test.jar
│ └── universalJavaApplicationStub
├── package.sh
└── src/
├── images/
│ └── manifest.mf
├── juicebox/
│ ├── CommandBroadcaster.java
│ ├── CommandExecutor.java
│ ├── CommandListener.java
│ ├── Context.java
│ ├── DirectoryManager.java
│ ├── HiC.java
│ ├── HiCGlobals.java
│ ├── IGVUtils.java
│ ├── MainWindow.java
│ ├── ProcessHelper.java
│ ├── Unused.java
│ ├── assembly/
│ │ ├── AssemblyFileExporter.java
│ │ ├── AssemblyFileImporter.java
│ │ ├── AssemblyHeatmapHandler.java
│ │ ├── AssemblyOperationExecutor.java
│ │ ├── AssemblyScaffoldHandler.java
│ │ ├── AssemblyStateTracker.java
│ │ ├── IGVFeatureCopy.java
│ │ ├── OneDimAssemblyTrackLifter.java
│ │ ├── PsfFileExporter.java
│ │ ├── PsfFileImporter.java
│ │ └── Scaffold.java
│ ├── data/
│ │ ├── AbstractDatasetReader.java
│ │ ├── BinReader.java
│ │ ├── Block.java
│ │ ├── BlockIndex.java
│ │ ├── ChromosomeHandler.java
│ │ ├── CombinedDatasetReader.java
│ │ ├── CombinedExpectedValueFunction.java
│ │ ├── ContactRecord.java
│ │ ├── CustomMatrixZoomData.java
│ │ ├── Dataset.java
│ │ ├── DatasetReader.java
│ │ ├── DatasetReaderFactory.java
│ │ ├── DatasetReaderV2.java
│ │ ├── DynamicBlock.java
│ │ ├── DynamicBlockIndex.java
│ │ ├── DynamicMatrixZoomData.java
│ │ ├── ExpectedValueFunction.java
│ │ ├── ExpectedValueFunctionImpl.java
│ │ ├── GeneLocation.java
│ │ ├── HiCFileLoader.java
│ │ ├── HiCFileTools.java
│ │ ├── Matrix.java
│ │ ├── MatrixZoomData.java
│ │ ├── NormFactorMapReader.java
│ │ ├── NormalizationVector.java
│ │ ├── ZoomAction.java
│ │ ├── ZoomActionTracker.java
│ │ ├── anchor/
│ │ │ ├── BEDTools.java
│ │ │ ├── GenericLocus.java
│ │ │ ├── GenericLocusParser.java
│ │ │ ├── GenericLocusTools.java
│ │ │ ├── MotifAnchor.java
│ │ │ ├── MotifAnchorParser.java
│ │ │ └── MotifAnchorTools.java
│ │ ├── basics/
│ │ │ ├── Chromosome.java
│ │ │ ├── ListOfDoubleArrays.java
│ │ │ ├── ListOfFloatArrays.java
│ │ │ └── ListOfIntArrays.java
│ │ ├── binary_file_layout.txt
│ │ ├── censoring/
│ │ │ ├── CustomMZDRegionHandler.java
│ │ │ ├── OneDimSearchUtils.java
│ │ │ ├── OneDimTrackCensoring.java
│ │ │ └── RegionPair.java
│ │ ├── feature/
│ │ │ ├── Feature.java
│ │ │ ├── FeatureFilter.java
│ │ │ ├── FeatureFunction.java
│ │ │ └── GenomeWideList.java
│ │ ├── iterator/
│ │ │ ├── BigContactRecordList.java
│ │ │ ├── ContactRecordIterator.java
│ │ │ ├── CoupledIteratorAndOffset.java
│ │ │ ├── GWIteratorContainer.java
│ │ │ ├── GenomeWideIterator.java
│ │ │ ├── IteratorContainer.java
│ │ │ ├── ListIteratorContainer.java
│ │ │ ├── ListOfListGenerator.java
│ │ │ ├── ListOfListIterator.java
│ │ │ ├── ListOfListIteratorContainer.java
│ │ │ ├── ParallelizedListOperations.java
│ │ │ └── ZDIteratorContainer.java
│ │ └── v9depth/
│ │ ├── ConstantDepth.java
│ │ ├── LogDepth.java
│ │ └── V9Depth.java
│ ├── encode/
│ │ ├── EncodeFileBrowser.java
│ │ ├── EncodeFileRecord.java
│ │ ├── EncodeTableModel.java
│ │ ├── UCSCEncodeUtils.java
│ │ ├── encode.hg18.txt
│ │ ├── encode.hg19.txt
│ │ ├── encode.hic.txt
│ │ ├── encode.mm9.txt
│ │ └── rnaChip.txt
│ ├── gui/
│ │ ├── BoundsPopupMenuListener.java
│ │ ├── MainMenuBar.java
│ │ ├── MainViewPanel.java
│ │ ├── PseudoCountEditor.java
│ │ └── SuperAdapter.java
│ ├── mapcolorui/
│ │ ├── BoundingBoxRenderer.java
│ │ ├── ColorRangeDialog.java
│ │ ├── ColorScaleHandler.java
│ │ ├── CursorRenderer.java
│ │ ├── Feature2DHandler.java
│ │ ├── FeatureRenderer.java
│ │ ├── GeneralTileManager.java
│ │ ├── HeatmapClickListener.java
│ │ ├── HeatmapMouseHandler.java
│ │ ├── HeatmapPanel.java
│ │ ├── HeatmapRenderer.java
│ │ ├── HiCMapTileManager.java
│ │ ├── JColorRangePanel.java
│ │ ├── OEColorScale.java
│ │ ├── PearsonColorScale.java
│ │ ├── PearsonColorScaleEditor.java
│ │ ├── RangeSlider.java
│ │ ├── RangeSliderUI.java
│ │ ├── ResolutionControl.java
│ │ └── ThumbnailPanel.java
│ ├── matrix/
│ │ ├── BasicMatrix.java
│ │ ├── DiskResidentBlockMatrix.java
│ │ ├── InMemoryMatrix.java
│ │ ├── RealMatrixWrapper.java
│ │ ├── SparseSymmetricMatrix.java
│ │ ├── SparseVector.java
│ │ └── SymmetricMatrix.java
│ ├── state/
│ │ ├── ImportStateFileDialog.java
│ │ ├── LoadStateFromXMLFile.java
│ │ ├── SaveFileDialog.java
│ │ ├── Slideshow.java
│ │ ├── State.java
│ │ ├── XMLFileHandling.java
│ │ ├── XMLFileParser.java
│ │ └── XMLFileWriter.java
│ ├── tools/
│ │ ├── HiCTools.java
│ │ ├── chrom/
│ │ │ └── sizes/
│ │ │ ├── ChromosomeSizes.java
│ │ │ ├── EBV.chrom.sizes
│ │ │ ├── GRCm38.chrom.sizes
│ │ │ ├── Pf3D7.chrom.sizes
│ │ │ ├── TAIR10.chrom.sizes
│ │ │ ├── aedAeg1.chrom.sizes
│ │ │ ├── anasPlat1.chrom.sizes
│ │ │ ├── assembly.chrom.sizes
│ │ │ ├── b37.chrom.sizes
│ │ │ ├── bTaurus3.chrom.sizes
│ │ │ ├── calJac3.chrom.sizes
│ │ │ ├── canFam3.chrom.sizes
│ │ │ ├── capHir1.chrom.sizes
│ │ │ ├── ce10.chrom.sizes
│ │ │ ├── dMel.chrom.sizes
│ │ │ ├── dm3.chrom.sizes
│ │ │ ├── dm6.chrom.sizes
│ │ │ ├── equCab2.chrom.sizes
│ │ │ ├── felCat8.chrom.sizes
│ │ │ ├── galGal4.chrom.sizes
│ │ │ ├── hg18.chrom.sizes
│ │ │ ├── hg19.chrom.sizes
│ │ │ ├── hg19_contig.chrom.sizes
│ │ │ ├── hg38.chrom.sizes
│ │ │ ├── loxAfr3.chrom.sizes
│ │ │ ├── macMul1.chrom.sizes
│ │ │ ├── macMulBaylor.chrom.sizes
│ │ │ ├── mm10.chrom.sizes
│ │ │ ├── mm9.chrom.sizes
│ │ │ ├── oryCun2.chrom.sizes
│ │ │ ├── oryLat2.chrom.sizes
│ │ │ ├── panTro4.chrom.sizes
│ │ │ ├── ratNor5.chrom.sizes
│ │ │ ├── ratNor6.chrom.sizes
│ │ │ ├── sCerS288c.chrom.sizes
│ │ │ ├── sacCer3.chrom.sizes
│ │ │ ├── spretus.chrom.sizes
│ │ │ └── susScr3.chrom.sizes
│ │ ├── clt/
│ │ │ ├── AggregateProcessing.java
│ │ │ ├── CLTFactory.java
│ │ │ ├── CommandLineParser.java
│ │ │ ├── CommandLineParserForJuicer.java
│ │ │ ├── JuiceboxCLT.java
│ │ │ ├── JuicerCLT.java
│ │ │ ├── UnitTests.java
│ │ │ ├── juicer/
│ │ │ │ ├── ABCompartmentsDiff.java
│ │ │ │ ├── APA.java
│ │ │ │ ├── Arrowhead.java
│ │ │ │ ├── CompareLists.java
│ │ │ │ ├── HiCCUPS.java
│ │ │ │ ├── HiCCUPS2.java
│ │ │ │ ├── HiCCUPSDiff.java
│ │ │ │ ├── HiCCUPSRegionHandler.java
│ │ │ │ ├── HiCCUPSscores.java
│ │ │ │ ├── Localizer.java
│ │ │ │ ├── LoopDomains.java
│ │ │ │ └── MotifFinder.java
│ │ │ └── old/
│ │ │ ├── AddNorm.java
│ │ │ ├── BPToFragment.java
│ │ │ ├── BigWig.java
│ │ │ ├── BinToPairs.java
│ │ │ ├── CalcKR.java
│ │ │ ├── CalcMatrixSum.java
│ │ │ ├── Dump.java
│ │ │ ├── Eigenvector.java
│ │ │ ├── FragmentToBed.java
│ │ │ ├── LibraryComplexity.java
│ │ │ ├── PairsToBin.java
│ │ │ ├── Pearsons.java
│ │ │ ├── PreProcessing.java
│ │ │ ├── SQLDatabase.java
│ │ │ ├── Statistics.java
│ │ │ ├── Summation.java
│ │ │ └── ValidateFile.java
│ │ ├── dev/
│ │ │ ├── APAvsDistance.java
│ │ │ ├── ChromosomeCalculation.java
│ │ │ ├── CompareVectors.java
│ │ │ ├── GeneFinder.java
│ │ │ ├── HiCArtMNDWriter.java
│ │ │ ├── IntraChromTriple.java
│ │ │ ├── MapSelectionPanel.java
│ │ │ ├── ParallelizedJuicerTools.java
│ │ │ ├── Private.java
│ │ │ ├── RandomUtils.java
│ │ │ ├── TripleCentroid.java
│ │ │ └── TriplesAPA.java
│ │ └── utils/
│ │ ├── Benchmark.java
│ │ ├── common/
│ │ │ ├── ArrayTools.java
│ │ │ ├── HiCFileUtils.java
│ │ │ ├── MatrixTools.java
│ │ │ ├── ShellCommandRunner.java
│ │ │ ├── StatPercentile.java
│ │ │ └── UNIXTools.java
│ │ ├── dev/
│ │ │ ├── AFAUtils.java
│ │ │ ├── LocalGenomeRegion.java
│ │ │ ├── LocationType.java
│ │ │ └── PearsonCorrelationMetric.java
│ │ ├── juicer/
│ │ │ ├── GeneTools.java
│ │ │ ├── apa/
│ │ │ │ ├── APADataStack.java
│ │ │ │ ├── APAPlotter.java
│ │ │ │ ├── APARegionStatistics.java
│ │ │ │ └── APAUtils.java
│ │ │ ├── arrowhead/
│ │ │ │ ├── ArrowheadScore.java
│ │ │ │ ├── ArrowheadScoreList.java
│ │ │ │ ├── BinnedScore.java
│ │ │ │ ├── BlockBuster.java
│ │ │ │ ├── BlockResults.java
│ │ │ │ ├── CumulativeBlockResults.java
│ │ │ │ ├── DynamicProgrammingUtils.java
│ │ │ │ ├── HighScore.java
│ │ │ │ ├── MatrixTriangles.java
│ │ │ │ └── connectedcomponents/
│ │ │ │ ├── BinaryConnectedComponents.java
│ │ │ │ └── IndexNode.java
│ │ │ ├── hiccups/
│ │ │ │ ├── GPUController.java
│ │ │ │ ├── GPUController2.java
│ │ │ │ ├── GPUHelper.java
│ │ │ │ ├── GPUOutputContainer.java
│ │ │ │ ├── GPUOutputContainer2.java
│ │ │ │ ├── GPUTesting.java
│ │ │ │ ├── HiCCUPS2Kernel_1kb.cu
│ │ │ │ ├── HiCCUPSConfiguration.java
│ │ │ │ ├── HiCCUPSKernel.cu
│ │ │ │ ├── HiCCUPSRegionContainer.java
│ │ │ │ └── HiCCUPSUtils.java
│ │ │ └── localizer/
│ │ │ └── LocalizerUtils.java
│ │ ├── norm/
│ │ │ ├── CustomNormVectorFileHandler.java
│ │ │ ├── ExternalNormalizationVectorLoader.java
│ │ │ ├── GenomeWideNormalizationVectorUpdater.java
│ │ │ ├── MultithreadedNormalizationVectorUpdater.java
│ │ │ ├── NormVectorInfo.java
│ │ │ ├── NormVectorUpdater.java
│ │ │ ├── NormalizationCalculations.java
│ │ │ ├── NormalizationTools.java
│ │ │ ├── NormalizationVectorIndexEntry.java
│ │ │ ├── NormalizationVectorUpdater.java
│ │ │ ├── NormalizedSum.java
│ │ │ ├── UnusedFunctions.java
│ │ │ ├── ZeroScale.java
│ │ │ └── final2/
│ │ │ ├── FinalScale.java
│ │ │ └── ScaleThreadObject.java
│ │ └── original/
│ │ ├── BigWigUtils.java
│ │ ├── BlockPP.java
│ │ ├── BlockQueue.java
│ │ ├── BlockQueueFB.java
│ │ ├── BlockQueueMem.java
│ │ ├── Chunk.java
│ │ ├── ContactCount.java
│ │ ├── ExpectedValueCalculation.java
│ │ ├── FragmentCalculation.java
│ │ ├── HiCDBUtils.java
│ │ ├── IndexEntry.java
│ │ ├── LargeIndexEntry.java
│ │ ├── MTIndexHandler.java
│ │ ├── MatrixPP.java
│ │ ├── MatrixZoomDataPP.java
│ │ ├── MultithreadedPreprocessor.java
│ │ ├── MultithreadedPreprocessorHic.java
│ │ ├── Preprocessor.java
│ │ ├── merge/
│ │ │ ├── HiCMergeTools.java
│ │ │ ├── StatsUtils.java
│ │ │ └── merger/
│ │ │ ├── GraphsMerger.java
│ │ │ ├── Merger.java
│ │ │ ├── PairedAlignmentStatsMerger.java
│ │ │ ├── SingleAlignmentStatsMerger.java
│ │ │ └── StatsMerger.java
│ │ ├── mnditerator/
│ │ │ ├── AlignmentPair.java
│ │ │ ├── AlignmentPairLong.java
│ │ │ ├── AsciiPairIterator.java
│ │ │ ├── AsciiToBinConverter.java
│ │ │ ├── BAMPairIterator.java
│ │ │ ├── BinPairIterator.java
│ │ │ ├── ComplexLineParser.java
│ │ │ ├── GenericPairIterator.java
│ │ │ ├── MNDFileParser.java
│ │ │ ├── MNDLineParser.java
│ │ │ ├── PairIterator.java
│ │ │ ├── RandomAccessAsciiPairIterator.java
│ │ │ ├── ShortBinPairIterator.java
│ │ │ ├── SimpleAsciiPairIterator.java
│ │ │ └── SimpleLineParser.java
│ │ └── stats/
│ │ ├── LoneStatisticsWorker.java
│ │ ├── ParallelStatistics.java
│ │ ├── ParallelStatisticsWorker.java
│ │ ├── StatisticsContainer.java
│ │ └── StatisticsWorker.java
│ ├── track/
│ │ ├── CategoryPanel.java
│ │ ├── EigenvectorTrack.java
│ │ ├── HiCCoverageDataSource.java
│ │ ├── HiCDataAdapter.java
│ │ ├── HiCDataPoint.java
│ │ ├── HiCDataSource.java
│ │ ├── HiCDataTrack.java
│ │ ├── HiCFeatureTrack.java
│ │ ├── HiCFixedGridAxis.java
│ │ ├── HiCFragmentAxis.java
│ │ ├── HiCGridAxis.java
│ │ ├── HiCIGVDataAdapter.java
│ │ ├── HiCLoadDialog.java
│ │ ├── HiCTrack.java
│ │ ├── HiCTrackManager.java
│ │ ├── HiCWigAdapter.java
│ │ ├── LoadAction.java
│ │ ├── LoadEncodeAction.java
│ │ ├── ResourceTree.java
│ │ ├── TrackConfigDialog.java
│ │ ├── TrackConfigDialog.jfd
│ │ ├── TrackConfigPanel.java
│ │ ├── TrackLabelPanel.java
│ │ ├── TrackPanel.java
│ │ ├── feature/
│ │ │ ├── AnnotationLayer.java
│ │ │ ├── AnnotationLayerHandler.java
│ │ │ ├── CustomAnnotationRTree2DHandler.java
│ │ │ ├── Feature2D.java
│ │ │ ├── Feature2DGuiContainer.java
│ │ │ ├── Feature2DList.java
│ │ │ ├── Feature2DParser.java
│ │ │ ├── Feature2DTools.java
│ │ │ ├── Feature2DWithMotif.java
│ │ │ ├── FeatureFilter.java
│ │ │ └── FeatureFunction.java
│ │ ├── tracksMenu_GalGal5.xml
│ │ ├── tracksMenu_hg19.xml
│ │ ├── tracksMenu_hg38.xml
│ │ ├── tracksMenu_mm10.xml
│ │ └── tracksMenu_mm9.xml
│ └── windowui/
│ ├── DisabledGlassPane.java
│ ├── DumpDialog.java
│ ├── EditFeatureAttributesDialog.java
│ ├── FileDropTargetListener.java
│ ├── GoToPanel.java
│ ├── HiCChromosomeFigPanel.java
│ ├── HiCKeyDispatcher.java
│ ├── HiCLayout.java
│ ├── HiCRulerPanel.java
│ ├── HiCZoom.java
│ ├── JBTreeCellRenderer.java
│ ├── JSplitButton.java
│ ├── LoadAssemblyAnnotationsDialog.java
│ ├── LoadDialog.java
│ ├── LoadModifiedAssemblyAnnotationsDialog.java
│ ├── MatrixType.java
│ ├── NormalizationHandler.java
│ ├── NormalizationType.java
│ ├── QCDialog.java
│ ├── RecentMenu.java
│ ├── SaveAssemblyDialog.java
│ ├── SaveImageDialog.java
│ └── layers/
│ ├── LayerPanelButtons.java
│ ├── LayersPanel.java
│ ├── Load2DAnnotationsDialog.java
│ ├── MiniAnnotationsLayerPanel.java
│ ├── PlottingStyleButton.java
│ ├── SaveAnnotationsDialog.java
│ ├── UnsavedAnnotationWarning.java
│ └── annotations2d.txt
├── org/
│ └── tc33/
│ └── jheatchart/
│ ├── HeatChart.java
│ ├── LICENSE.MD
│ └── README.MD
└── resources/
└── manifests/
├── juicebox_jar/
│ └── META-INF/
│ └── MANIFEST.MF
└── juicer_tools_jar/
└── META-INF/
└── MANIFEST.MF
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows 10, macOS Mojave, etc]
- Version [e.g. 1.10.11]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/custom.md
================================================
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .gitignore
================================================
# Created by .gitignore support plugin (hsz.mobi)
*.iml
.idea/workspace.xml
*.DS_Store
out/
.vscode
================================================
FILE: .idea/artifacts/Juicebox.xml
================================================
$PROJECT_DIR$/out/artifacts/Juicebox
================================================
FILE: .idea/artifacts/JuicerTools.xml
================================================
$PROJECT_DIR$/out/artifacts/JuicerTools
================================================
FILE: .idea/compiler.xml
================================================
================================================
FILE: .idea/copyright/Juicebox.xml
================================================
================================================
FILE: .idea/copyright/profiles_settings.xml
================================================
================================================
FILE: .idea/encodings.xml
================================================
================================================
FILE: .idea/jarRepositories.xml
================================================
================================================
FILE: .idea/kotlinc.xml
================================================
================================================
FILE: .idea/libraries/KotlinJavaRuntime.xml
================================================
================================================
FILE: .idea/libraries/broadinstitute.xml
================================================
================================================
FILE: .idea/libraries/general.xml
================================================
================================================
FILE: .idea/libraries/jargs.xml
================================================
================================================
FILE: .idea/libraries/jcuda.xml
================================================
================================================
FILE: .idea/libraries/jfreechart_1_0_19.xml
================================================
================================================
FILE: .idea/libraries/log4j_core_2_11_0.xml
================================================
================================================
FILE: .idea/libraries/npy.xml
================================================
================================================
FILE: .idea/misc.xml
================================================
================================================
FILE: .idea/modules.xml
================================================
================================================
FILE: .idea/scopes/Juicebox_Source.xml
================================================
================================================
FILE: .idea/scopes/scope_settings.xml
================================================
================================================
FILE: .idea/uiDesigner.xml
================================================
================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at aidenlab@bcm.edu. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Juicebox and/or Juicebox Assembly Tools
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
The following is a set of guidelines for contributing to Juicebox and its modules, which are hosted in the [Aiden Lab Repositories](https://github.com/aidenlab) on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
## Code of Conduct
This project and everyone participating in it is governed by the [Juicebox Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [aidenlab@bcm.edu](mailto:aidenlab@bcm.edu).
## I don't want to read this whole thing I just have a question!!!
> **Note:** Please don't file an issue to ask a question. You'll get faster results by using our [3D-Genomics Official Forum](https://aidenlab.org/forum.html). We have an official message board where the community chimes in with helpful advice if you have questions.
While we strongly suggest using the public forum so that the community at large benefits from the discussion, you can also send us an email at [aidenlab@bcm.edu](mailto:aidenlab@bcm.edu) for questions related to sensitive datasets.
## **Did you find a bug?**
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/aidenlab/Juicebox/issues).
* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/aidenlab/juicebox/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample, executable test case, or clear set of instructions** demonstrating the expected behavior that is not occurring.
## **Did you write a patch that fixes a bug?**
* Open a new GitHub pull request with the patch.
* Ensure the Pull Request description clearly describes the problem and solution. Include the relevant issue number if applicable.
## **Did you fix whitespace, format code, or make a purely cosmetic patch?**
We welcome changes that are cosmetic in nature and do not necessarily add anything to the stability, functionality, or testability of Juicebox. Please follow commonly practiced Java Style Guides when doing so.
## **Do you intend to add a new feature or change an existing one?**
* Suggest your change by creating a Github issue! Reach out to us at [aidenlab@bcm.edu](mailto:aidenlab@bcm.edu) for advice and start writing code!
## **Do you have questions about the source code?**
* Ask any question about how to use Juicebox and Juicebox Assembly Tools in the [3D Genomics Official Forum](https://aidenlab.org/forum.html).
## **Do you want to contribute to the Juicebox documentation?**
* Reach out to us at [aidenlab@bcm.edu](mailto:aidenlab@bcm.edu).
Juicebox and Juicebox Assembly Tools is a volunteer effort. We encourage you to pitch in and join the team!
Thanks! :heart: :heart: :heart:
The Center for Genome Architecture
Baylor College of Medicine & Rice University
================================================
FILE: HiCFormatV8.md
================================================
# hic file format
## Structure
* Header
* Body
* Matrix
* Block
* Footer
* Master index
* Expected value vectors
## Header
|Field | Description | Type | Value |
|------|------------|------|-------|
|Magic|HiC magic string|String|HIC|
|Version|Version number|int|8|
|footerPosition|File position of the Footer section, containing the master index, expected values, and normalization vectors. |long||
|genomeId| Genome identifier (e.g. hg19, mm9, etc)| String||
||||
|nAttributes |Number of key-value pair attributes| int||
||*List of key-value pair attributes (n = nAttributes). See notes on common attributes below.*||
|key |Attribute key| String ||
|value|Attribute value| String||
|||||
|nChrs| Number of chromosomes|int||
||*List of chromosome lengths (n = nChrs)*||
|chrName |Chromosome name |String||
|chrLength| Chromosome length | int ||
|||||
|nBpResolutions |Number of base pair resolutions| int||
||*List of bin sizes for bp resolution levels (n = nBpResolutions)*||
|resBP |Bin size in base pairs |int||
|||||
|nFragResolutions |Number of fragment resolutions |int||
||*List of bin sizes for frag resolution levels (n = nFragResolutions)*||
|resFrag |Bin size in fragment units (1, 2, 5, etc)| int||
|||||
||*List of fragment site positions per chromosome, in same order as chromosome list above (n = nChrs). This section absent if nFragResolutions = 0.*||
|nSites| Number of sites for this chromosome| int||
||*List of sites (n = nSites)*||
|sitePosition| Site position in base pairs| int||
## Body
The **Header** section is followed immediatly by the **Body**, which containe the contact map data for each
chromosome-chromosome pairing and each resolution.
### Matrix metadata
This section contains metadata for the contact matrices. It is repeated for all each chromosome-chromosome pair.
The master index contains an entry for each combination and is used to randomly access a specific
matrix as needed. The metadata in this section includes an index for data blocks which contain the actual
contact data.
|Field |Description| Type| Value|
|------|------------|------|-------|
|chr1Idx| Index for chromosome 1. This is the index into the array of chromosomes defined in the header above. The first chromosome has index **0**.| int||
|chr2Idx| Index for chromosome 2. | int ||
|nResolutions |Total number of resolutions for this chromosome-chromosome pair, including base pair and fragment resolutions. |int||
||*Resolution metadata. Repeat for each resolution. (n = nResolutions)*||
|unit| Distance unit, base-pairs or fragments |String |BP or FRAG|
|resIdx |Index number for this resolution level, an Array index into the bin size list of the header, first element is **0**. | int||
|sumCounts| Sum of all counts (or scores) across all bins at current resolution.| float||
|occupiedCellCount| Total count of cells that are occupied. **Not currently used**|int|0|
|percent5| Estimate of 5th percentile of counts among occupied bins. **Not currently used**|float|0|
|percent95| Estimate of 95th percentile of counts among occupied bins **Not currently used**|float|0|
|binSize| The bin size in base-pairs or fragments |int||
|blockSize |Dimension of each block in bins. Blocks are square, so the total number of bins is ```blockSize^2```. See description of grid strcture below|int||
|blockColumnCount|The number of columns in the grid of blocks. |int||
|blockCount|The number of blocks stored in the file. Note empty blocks are not stored.|||
|||||
|*Block index. Repeat for each resolution (n = nResolutions)*||
|blockNumber |Numeric id for block. This is the linear position of the block in the grid when counted in row-major order. ```blockNumber = column * blockColumnCount + row``` where first row and column **0** |int|
|blockPosition| File position of block| long|
|blockSizeBytes |Size of block in bytes| int|
||||
||*Block data*||
| blocks | Compressed blocks for all matrices and resolutions. See description below. |||
#### Block
A block represents a square sub-matrix of a contact map.
***Note: Blocks are indivdually compressed with ZLib***
|Field |Description| Type| Value|
|------|------------|------|-------|
|nRecords |Number or contact records in this block| int |
|binXOffset | X offset for the contact records in this block. The binX value below is relative to this offset.||
|binYOffset | Y offset for the contact records in this block. The binX value below is relative to this offset.
|useFloat | Flag indicating the ```value``` field in contact records for this block are recorded with data type ```float```. If == 1 a ```float``` is used, otherwise type is ```short```| byte |
|matrixRepresentation | Representation of matrix used for the contact records. If == 1 the representation is a ```list of rows```, if == 2 ```dense```. | byte |
|blockData| The block matrix data. See descriptions below, also in the notes section.
##### Block data - list of rows
|Field |Description| Type| Value|
|------|------------|------|-------|
|rowCount | Number or rows | short ||
||
|*rows (n = rowCount)*
|rowNumber | Matrix row number, first row is ```0``` | short ||
|recordCount | Number of records for this row. Row is sparse, zeroes are not recorded. | short ||
||
|*contact records (n = cellCount)*||
|binX |X axis index| short||
|value |Value (counts or score). The data type is determined by the ```useFloat``` flag above.| float : short||
##### Block data - dense
|Field |Description| Type| Value|
|------|------------|------|-------|
|nRecords | Number of contact records in this block. | int ||
|w | Width of the dense block. This can be < the blockSize if the edge columns on either side are zeroes. See discussion on block representation below | short ||
||
|*contact records (n = nRecords)*||
|value |Value (counts or score). The data type is determined by the ```useFloat``` flag above.| float : short||
### Footer
| Field | Description| Type | Value |
|------|------------|------|-------|
|nBytesV5| Number of bytes for the “version 5” footer, that is everything up to the normalized expected vectors. This field (*nBytesV5*) is not included, so the total number of bytes between ```footerPosition``` and ```nNormVectors``` is ```nBytesV5 + 4```. |int||
#### Master index
| Field | Description| Type | Value |
|------|------------|------|-------|
|nEntries| Number of index entries| int||
||
||*List of index entries (n = nEntries)*||
|key| A key constructed from the indeces of the two chromosomes for this matrix. The indeces are defined by the list of chromosomes in the header section with the first chromosome occupying index **0**|String||
|position |Position of the start of the chromosome-chromosome matrix record in bytes |long||
|size |Size of the chromosome-chromsome matrix record in bytes. This does not include the **Block** data.| int||
#### Expected value vectors
| Field | Description| Type | Value |
|------|------------|------|-------|
|nExpectedValueVectors| Number of expected value vectors to follow. These are expected values from the non-normalized observed matrix.| int|
||
||*List of expected value vectors (n = nExpectedValueVectors)*||
|unit| Bin units either FRAG or BP. |String |FRAG : BP|
|binSize |Bin (grid) size for this calculation |int||
|nValues |Size of the vector| int||
||
|*List of expected values (n = nValues)*|
|value |Expected value| double||
|nChrScaleFactors| Number of chromosome normalization factors| int||
||
||*List of normalization factors (n = nChrScaleFactors)*||
|chrIndex| Chromosome index| int||
|chrScaleFactor| Chromosome scale factor |double||
#### Normalized expected value vectors
| Field | Description| Type | Value |
|------|------------|------|-------|
|nNormExpectedValueVectors| Number of normalized expected value vectors to follow |int||
||
|*List of normalized vectors (n = nNormExpectedValueVectors)*||
|type| Indicates type of normalization |String| VC:KR:INTER_KR:INTER_VC:GW_KR:GW_VC|
|unit |Bin units either FRAG or BP. |String| FRAG : BP|
|binSize| Bin (grid) size for this calculation |int||
|nValues| Size of the vector |int ||
||
||*List of expected values (n = nValues)*||
|value |Expected value |double||
||
|nChrScaleFactors|Number of normalizatoin factos for this vector|||
||*List of normalization factors (n = nChrScaleFactors)*||
|chrIndex| Chromosome index |int ||
|chrScaleFactor| Chromosome scale factor |double||
#### Normalization vectors
| Field | Description| Type | Value |
|------|------------|------|-------|
|nNormVectors| Number of normalization vectors | int||
||*List of normalization vectors (n= nNormalizationVectors)*||
|type |Indicates type of normalization |String| VC:KR:INTER_KR:INTER_VC:GW_KR:GW_VC|
|chrIdx| Chromosome index |int| |
|unit| Bin units either FRAG or BP.| String| FRAG : BP|
|binSize |Resolution |int||
|position| File position of value array| long ||
|nBytes| Size in bytes of value array |int ||
||*Normalization vector arrays (repeat for each entry above)*||
|nValues| Number of values in array| int||
||*Normalization vector values (n= nValues)*||
#### Notes
##### Data types
* Strings are null (0) terminated. So for example the string "HIC" is represented by 4 bytes [48 49 43 0]
* Other data types are Java
* short - 16 bit integer
* int - 32 bit integer
* long - 64 bit integer
* float - 32 bit floating point
* double - 64 bit floating point
##### Attributes
The attributes table in the header can contain an arbitrary number of key-value string pairs. The **Juicer** tool
inserts one or more of the following attributes.
* "statistics":
* "graphs":
* "software":
* "nviIndex": reserved for future use
* "nviLength": reserved for future use
#### Grid structure
Each chr-chr matrix at a given resolution is subdivided into a grid structure of square **blocks**.
Each block consists of NxN bins, where N is referred to as **blockSize**. In older versions of the spec,
and in code, this parameter is referred to as **blockBinCount**.
For intra chromosome matrices (chr1 == chr2) only the lower diagonal is stored (row >= column). The upper diagonal
can be inferred upon reading by tansposition.
#### Block matrix representation
The spatial unit for a block is a ```bin```, which can be computed from a genomic position with the formulat
```bin = floor(position / binSize)```.
The origin of a block is then
```floor(x / binsSize), floor(y / binSize)```
where x and y are genomic positions in either base pairs or fragment number, depending on the
* List of rows
The list of rows is a sparse matrix format. Each row is represented as follows
```rowNumber rowSize [binX1 value1, binX2 value2, ...]```
The first row in the matrix has ```rowNumber = 0```. The highest row number possible is ```blockSize - 1```
* Dense
In dense matrix format all values including zero are output in row major order. Allowance is made however for the
possibility that only a sub-matrix of the block is populated, specifically that leading or trailing columns of
the block might have no contacts (value = 0). To account for this possibility the maximum column number within the block
which has at least 1 non-zero value is determined, which we will call ```binXMax```. The width of the block can
then be determined and used to obtain the x and y coordinates in bin units for each value as follows.
w = (binXMax - binXOffset + 1);
row = floor(i / w);
col = i - row * w;
binX = binXOffset + col;
binY = binYOffset + row;
================================================
FILE: Juicer.README
================================================
# Juicer
Juicer is a platform for analyzing kilobase resolution Hi-C data. In this distribution,
we include the pipeline for generating Hi-C maps from fastq raw data files and command
line tools for feature annotation on the Hi-C maps.
----------------------
Example for Reviewers
----------------------
We've provided a step-by-step guide to showcase some of the features of
Juicer. If you run into problems, see below for more detailed documentation.
This example runs on Amazon Web Services, but you can install the pipeline
on any LSF, Univa Grid Engine, or SLURM cluster.
1. Make sure you're in the top-level directory, with this README and the
Juicer_AWS.pem file. (NOTE: please do not share this .pem file with anyone)
2. You were given an anonymous IP address. At a command line prompt, type:
ssh -i Juicer_AWS.pem ubuntu@
3. This will log you into an AWS instance that contains all the software
needed to run the pipeline. Type
cd /opt/juicer/work/
4. We will run the pipeline on a test dataset of a single chromosome of the primary+
replicate map from (Rao+Huntley et al., 2014). Type:
cd MBR19
5. Run the Juicer pipeline on the raw data, which is stored in the fastq
directory:
/opt/juicer/scripts/juicer.sh -g hg19 -s MboI
6. You will see a series of messages sending jobs to the cluster. Do not
kill the script or close the server connection until you see:
“(-: Finished adding all jobs... please wait while processing.”
7. At this point you can close the connection and come back later.
To see the progress of the pipeline as it works, type:
bjobs -w
7. Eventually the bjobs command will report “No unfinished job found”. Type:
tail lsf.out
You should see “(-: Pipeline successfully completed (-:”
8. Results are available in the aligned directory. The Hi-C maps are in
inter.hic (for MAPQ > 0) and inter_30.hic (for MAPQ >= 30). The Hi-C maps
can be loaded in Juicebox and explored. They can also be used for
automatic feature annotation and to extract matrices at specific
resolutions.
These results also include automatic feature annotation. The output files include
a genome-wide annotation of loops and, whenever possible, the CTCF motifs that anchor
them (identified using the HiCCUPS algorithm). The files also include a genome-wide
annotation of contact domains (identified using the Arrowhead algorithm). The formats
of these files are described in the Juicebox tutorial online; both files can be loaded
into Juicebox as a 2D annotation.
9. To download a file (e.g. inter.hic) from AWS to load into Juicebox, type:
sftp -i Juicer_AWS.pem ubuntu@
cd /opt/juicer/work/MBR19/aligned
get inter.hic
get inter_30.hic
get ... (each of hiccups, apa, motifs, and arrowhead output files)
10. You can also run the pipeline on genome-wide dataset that is lower resolution. Type
cd /opt/juicer/work/HIC003
Then
/opt/juicer/scripts/juicer.sh -g hg19 -s MboI
Again the pipeline will run. The results will be available in the aligned directory.
Because this is not a deeply sequenced map, loop lists and domain lists will not be
produced.
See below for more documentation.
------------
Distribution
------------
The files included on the AWS distribution are in /opt/juicer and are:
*references - Genome references
*restriction_sites - Restriction positions for combinations of reference genome and restriction enzymes
*scripts - Juicer main scripts
*work - Data samples
*hic_files - Internal property file
In this zip file, we include the scripts for running Juicer on LSF,
Univa Grid Engine, and SLURM
/AWS - scripts for running pipeline and postprocessing on AWS
/UGER - scripts for running pipeline and postprocessing on UGER
/SLURM - scripts for running pipeline and postprocessing on SLURM
/juicebox_tools - source files for postprocessing algorithms
/Juicer_AWS.pem - ssh key to access anonymous AWS server
----------------------------------
Hardware and Software Requirements
----------------------------------
Juicer is a pipeline optimized for parallel computation on a cluster. Juicer
consists of two parts: the pipeline that creates Hi-C files from raw data,
and the post-processing command line tools.
*
* Cluster requirements:
*
Juicer requires the use of a cluster, with ideally >= 4 cores (min 1 core)
and >= 64 GB RAM (min 16 GB RAM)
Juicer currently works with the following resource management software:
- OpenLava (http://www.openlava.org/)
- LSF (http://www-03.ibm.com/systems/services/platformcomputing/lsf.html)
- SLURM (http://slurm.schedmd.com/download.html)
- GridEngine (Univa, etc. any flavor)
*
* Command line tool requirements:
*
The minimum software requirement to run Juicer is a working Java installation
(version >= 1.7) on Windows, Linux, and Mac OSX. We recommend using the
latest Java version available, but please do not use the Java Beta Version.
Minimum system requirements for running Java can be found at
http://java.com/en/download/help/sysreq.xml
To download and install the latest Java Runtime Environment (JRE), please go
to http://www.java.com/download
*
* GNU CoreUtils
*
The latest version of GNU coreutils can be downloaded from
https://www.gnu.org/software/coreutils/manual/
*
* Burrows-Wheeler Aligner (BWA)
*
The latest version of BWA should be installed from
http://bio-bwa.sourceforge.net/
*
* CUDA (for HiCCUPS peak calling)
*
You must have an NVIDIA GPU to install CUDA
Instructions for installing the latest version of CUDA can be found
on the NVIDIA Developer site:
https://developer.nvidia.com/cuda-downloads
The native libraries included with Juicer are compiled for CUDA 7.
Other versions of CUDA can be used, but you will need to download the
respective native libraries from
http://www.jcuda.org/downloads/downloads.html
For best performance, use a dedicated GPU. You may also be able to obtain
access to GPU clusters through Amazon Web Services or a local research
institution.
*
* Java 1.7 or 1.8 JDK (for compiling from source files)
*
The instructions here are for the Java 1.8 JDK.
For Windows/Mac/Linux, the Java 1.8 JDK can be installed from here:
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
(Alternative) For Ubuntu/LinuxMint
http://tecadmin.net/install-oracle-java-8-jdk-8-ubuntu-via-ppa/
*
* Apache Ant (for compiling from source files)
*
Mac
Ant should be installed on most Macs. To verify installation via the
command prompt, type
ant -version
If Ant is not on your Mac, install it via homebrew. At the command prompt,
type
brew update
brew install ant
You may need to install Homebrew (http://brew.sh/) on your machine
See the following Stackoverflow post for more details:
http://stackoverflow.com/questions/3222804/how-can-i-install-apache-ant-on-mac-os-x
Windows
Installing Ant requires some minor changes to your system environment. Follow the instructions in this article:
http://www.nczonline.net/blog/2012/04/12/how-to-install-apache-ant-on-windows/
Linux
In the command prompt, type
sudo apt-get install ant
or
sudo yum install ant
depending on your package installer
--------------------------------
Compiling Jars from Source Files
--------------------------------
1. You should have Java 1.7 (or 1.8) JDK and Apache Ant installed on your system. See
below for more information.
2. Go to the folder containing the Juicebox source files and edit the
juicebox.properties file with the proper Java JDK Address.
3. Open the command line, navigate to the folder containing the build.xml file
and type
ant
The process should take no more than a minute to build on most machines.
4. The jars are written to the directory out/. You can change this by editing
the build.xml file.
-------------
Documentation
-------------
We have extensive documentation below for how to use Juicer.
------------------------
Command Line Tools Usage
------------------------
To launch the command line tools, use the shell script “juicebox.sh” on Unix/MacOS
or type
java -jar juicebox_tools.jar (command...) [flags...]
For HiCCUPS loop calling without the shell or bat script, you will need to
call:
java -Xms512m -Xmx2048m -Djava.library.path=path/to/natives/
-jar Juicebox_CLT.jar hiccups [flags...]
where path/to/natives is the path to the native libraries used for Jcuda
By default, these are located in the lib/jcuda folder.
In the command line tools, there are 4 functions:
> "apa" for conducting aggregate peak analysis
> "hiccups" for annotating loops
> "motifs" for finding CTCF motifs
> "arrowhead" for annotating contact domains
The "juicebox.sh” (Unix/MacOS) script can be used in place of the unwieldy
"java -Djava.library.path=path/to/natives/ -jar juicebox_tools.jar"
---
APA
---
The "apa" command takes three required arguments and a number of optional
arguments.
apa [-n minval] [-x maxval] [-w window] [-r resolution(s)] [-c chromosome(s)]
[-k NONE/VC/VC_SQRT/KR]
The required arguments are:
: Address of hic file(s) which should end with ".hic". This is the file you will
load into Juicebox. URLs or local addresses may be used. To sum multiple hic Files together,
use the '+' symbol between the addresses (no whitespace between addresses)
: List of peaks in standard 2D feature format (chr1 x1 x2 chr2 y1 y2 color ...)
: Working directory where outputs will be saved
The optional arguments are:
-n minimum distance away from the diagonal. Used to filter peaks too close to the diagonal.
Units are in terms of the provided resolution. (e.g. -n 30 @ resolution 5kB will filter loops
within 30*(5000/sqrt(2)) units of the diagonal)
-x maximum distance away from the diagonal. Used to filter peaks too far from the diagonal.
Units are in terms of the provided resolution. (e.g. -n 30 @ resolution 5kB will filter loops
further than 30*(5000/sqrt(2)) units of the diagonal)
-w width of region to be aggregated around the specified loops (units of resolution)
-r resolution for APA; multiple resolutions can be specified using commas (e.g. 5000,10000)
-c Chromosome(s) on which APA will be run. The number/letter for the chromosome can be
used with or without appending the "chr" string. Multiple chromosomes can be specified using
commas (e.g. 1,chr2,X,chrY)
-k Normalizations (case sensitive) that can be selected. Generally, KR (Knight-Ruiz)
balancing should be used when available.
Default settings of optional arguments:
-n 30
-x (infinity)
-w 10
-r 25000,10000
-c (all chromosomes)
-k KR
------------
APA Examples
------------
apa HIC006.hic all_loops.txt results1
> This command will run APA on HIC006 using loops from the all_loops files
> and save them under the results1 folder.
apa https://hicfiles.s3.amazonaws.com/hiseq/gm12878/in-situ/combined.hic
all_loops.txt results1
> This command will run APA on the GM12878 mega map using loops from the all_loops
> files and save them under the results1 folder.
apa -r 10000,5000 -c 17,18 HIC006.hic+HIC007.hic all_loops.txt results
> This command will run APA at 50 kB resolution on chromosomes 17 and 18 for the
> summed HiC maps (HIC006 and HIC007) using loops from the all_loops files
> and save them under the results folder
-------
HiCCUPS
-------
hiccups [-m matrixSize] [-c chromosome(s)] [-r resolution(s)] [-k normalization (NONE/VC/VC_SQRT/KR)] [-f fdr] [-p peak width] [-i window]
[-t thresholds] [-d centroid distances]
The required arguments are:
: Address of hic file which should end with ".hic". This is the file you will
load into Juicebox. URLs or local addresses may be used.
: Final list of all loops found by HiCCUPS. Can be visualized directly in Juicebox as a 2D annotation.
By default, various values critical to the HICCUPS algorithm are saved as attributes for each loop found. These can be
disabled using the suppress flag below.
The optional arguments are:
-m Maximum size of the submatrix within the chromosome passed on to GPU (Must be an even number greater than 40
to prevent issues from running the CUDA kernel). The upper limit will depend on your GPU. Dedicated GPUs
should be able to use values such as 500, 1000, or 2048 without trouble. Integrated GPUs are unlikely to run
sizes larger than 90 or 100. Matrix size will not effect the result, merely the time it takes for hiccups.
Larger values (with a dedicated GPU) will run fastest.
-c Chromosome(s) on which HiCCUPS will be run. The number/letter for the chromosome can be used with or
without appending the "chr" string. Multiple chromosomes can be specified using commas (e.g. 1,chr2,X,chrY)
-r Resolution(s) for which HiCCUPS will be run. Multiple resolutions can be specified using commas
(e.g. 25000,10000,5000). Due to the nature of DNA looping, it is unlikely that loops will be found at
lower resolutions (i.e. 50kB or 100kB)
IMPORTANT: if multiple resolutions are used, the flags below can be configured so that different parameters are
used for the different resolutions.
-k Normalizations (case sensitive) that can be selected. Generally, KR (Knight-Ruiz)
balancing should be used when available.
-f FDR values actually corresponding to max_q_val (i.e. for 1% FDR use 0.01, for 10%FDR use 0.1). Different
FDR values can be used for each resolution using commas. (e.g "-r 5000,10000 -f 0.1,0.15" would run HiCCUPS at
10% FDR for resolution 5000 and 15% FDR for resolution 10000)
-p Peak width used for finding enriched pixels in HiCCUPS. Different peak widths can be used for each
resolution using commas. (e.g "-r 5000,10000 -p 4,2" would run at peak width 4 for resolution 5000 and
peak width 2 for resolution 10000)
-i Window width used for finding enriched pixels in HiCCUPS. Different window widths can be used for each
resolution using commas. (e.g "-r 5000,10000 -p 10,6" would run at window width 10 for resolution 5000 and
window width 6 for resolution 10000)
-t Thresholds for merging loop lists of different resolutions. Four values must be given, separated by
commas (e.g. 0.02,1.5,1.75,2). These thresholds (in order) represent:
> threshold allowed for sum of FDR values of the horizontal, vertical, donut, and bottom left filters
(an accepted loop must stay below this threshold)
> threshold ratio that both the horizontal and vertical filters must exceed
> threshold ratio that both the donut and bottom left filters must exceed
> threshold ratio that at least one of the donut and bottom left filters must exceed
-d Distances used for merging nearby pixels to a centroid. Different distances can be used for each
resolution using commas. (e.g "-r 5000,10000 -d 20000,21000” would merge pixels within 20kB of each
other at 5kB resolution and within 21kB at 10kB resolution.
Defaults:
Medium resolution maps:
-m 512
-c (all chromosomes)
-r 10000
-k KR
-f .1
-p 2
-i 5
-t 0.02,1.5,1.75,2
-d 20000,20000,50000
High resolution maps:
-m 512
-c (all chromosomes)
-r 5000,10000
-k KR
-f .1,.1
-p 4,2
-i 7,5
-t 0.02,1.5,1.75,2
-d 20000,20000,50000
----------------
HiCCUPS Examples
----------------
hiccups HIC006.hic all_hiccups_loops
> This command will run HiCCUPS on HIC006 and save all found loops to the all_hiccups_loops files
hiccups -m 500 -r 5000,10000 -f 0.1,0.1 -p 4,2 -i 7,5 -d 20000,20000,0 -c 22 HIC006.hic all_hiccups_loops
> This command will run HiCCUPS on chromosome 22 of HIC006 at 5kB and 10kB resolution using the following values:
>> 5kB: fdr 10%, peak width 4, window width 7, and centroid distance 20kB
>> 10kB: fdr 10%, peak width 2, window width 5, and centroid distance 20kB
> The resulting loop list will be merged and saved as all_hiccups_loops
> Note that these are values used for generating the GM12878 loop list
-------
Arrowhead
-------
arrowhead [-c chromosome(s)] [-m matrix size] [-r resolution] [-k normalization (NONE/VC/VC_SQRT/KR)] " +
" [feature_list] [control_list]
The required arguments are:
: Address of hic file(s) which should end with ".hic". This is the file you will
load into Juicebox. URLs or local addresses may be used. To sum multiple hic Files together,
use the '+' symbol between the addresses (no whitespace between addresses)
: Final list of all contact domains found by Arrowhead. Can be visualized directly in Juicebox
as a 2D annotation.
-- NOTE -- If you want to find scores for a feature and control list, both must be provided:
[feature_list]: Feature list of loops/domains for which block scores are to be calculated
[control_list]: Control list of loops/domains for which block scores are to be calculated
The optional arguments are:
-c Chromosome(s) on which Arrowhead will be run. The number/letter for the chromosome can be used with or
without appending the "chr" string. Multiple chromosomes can be specified using commas (e.g. 1,chr2,X,chrY)
-m Size of the sliding window along the diagonal in which contact domains will be found. Must be an even
number as (m/2) is used as the increment for the sliding window. (Default 2000)
-r resolution for which Arrowhead will be run. Generally, 5kB (5000) or 10kB (10000)
resolution is used depending on the depth of sequencing in the hic file(s).
-k Normalizations (case sensitive) that can be selected. Generally, KR (Knight-Ruiz)
balancing should be used when available.
Default settings of optional arguments:
Medium resolution maps:
-c (all chromosomes)
-m 2000
-r 10000
-k KR
High resolution maps:
-c (all chromosomes)
-m 2000
-r 5000
-k KR
----------------
Arrowhead Examples
----------------
NOTE: Arrowhead will choose appropriate defaults for hic files if no specifications are given
arrowhead https://hicfiles.s3.amazonaws.com/hiseq/ch12-lx-b-lymphoblasts/in-situ/combined_30.hic contact_domains_list
This command will run Arrowhead on a mouse cell line HiC map (medium resolution) at resolution 10 kB and save all
contact domains to the contact_domains_list file. These are the settings used to generate the official contact
domain list on the ch12-lx-b-lymphoblast cell line.
arrowhead https://hicfiles.s3.amazonaws.com/hiseq/gm12878/in-situ/combined_30.hic contact_domains_list
This command will run Arrowhead at resolution 5kB on the GM12878 HiC map (high resolution) and save all contact
domains to the contact_domains_list file. These are the settings used to generate the official GM12878
contact domain list.
-----------------------
Motif Finder
-----------------------
motifs [custom_global_motif_list]
The required arguments are:
: hg19 supported by default. For other genome assemblies, provide a
custom_global_motif_list in FIMO format.
File path to a directory (e.g. ) which contains two folders: "unique" and
"inferred". These folders should contain a combination of RAD21, SMC3, and CTCF BED files.
By intersecting these 1D tracks, the strongest peaks will be identified. Unique motifs
generally use a more stringent combination of BED files than inferred motifs.
: List of peaks in standard 2D feature format (chr1 x1 x2 chr2 y1 y2 color ...)
-- NOTE -- If you want to use a custom list of potential motifs:
[custom_global_motif_list]: Motif list output using FIMO format can be used as an alternative
to the internal motif list
-------------------
Motif Finder Examples
-------------------
Assuming the following file structure is present:
/path/to/local/bed/files/unique/CTCF.bed
/path/to/local/bed/files/unique/RAD21.bed
/path/to/local/bed/files/unique/SMC3.bed
/path/to/local/bed/files/inferred/CTCF.bed
motifs hg19 /path/to/local/bed/files /gm12878_hiccups_loops.txt
This command will find motifs from the internal hg19 motif list for the loops in gm12878_hiccups_loops.txt
and save them to gm12878_hiccups_loops_with_motifs.txt. The CTCF, RAD21, and SMC3 BED files will be used
together (i.e. intersected) to find unique motifs. Just the CTCF track will be used to infer best motifs.
motifs hg19 /path/to/local/bed/files gm12878_hiccups_loops.txt hg_19_custom_motif_list.txt
This command will find motifs from hg_19_custom_motif_list.txt for the loops in gm12878_hiccups_loops.txt
and save them to gm12878_hiccups_loops_with_motifs.txt. The CTCF, RAD21, and SMC3 BED files will be used
together (i.e. intersected) to find unique motifs. Just the CTCF track will be used to infer best motifs.
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 Aiden Lab
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
--------------
Breaking News!
--------------
This codebase is in the process of being split up to better support long-term maintenance and modular aspects of different tools
available. The new repositories will be:
- [JuiceboxGUI](https://github.com/aidenlab/juiceboxgui) for visualization of Hi-C maps with Juicebox Desktop and genome assembly correction with Assembly Tools.
- [HiCTools](https://github.com/aidenlab/hictools) for building and writing .hic files (Pre, Addnorm, and Statistics)
- [JuicerTools](https://github.com/aidenlab/juicertools) for downstream analysis of .hic files (HiCCUPS, Arrowhead, APA, etc.)
- [Java Straw](https://github.com/aidenlab/java-straw) to quickly read and stream data from .hic files into Java, and is used by the above repositories.
We also have new tools:
- [Straw](https://github.com/aidenlab/straw) to quickly read and stream data from .hic files into C++, python, R, and MATLAB.
- [POSSUMM](https://github.com/sa501428/EigenVector) for new C++ code to rapidly calculate A-B compartments (i.e.
eigenvectors) for higher resolutions
- [EMT](https://github.com/sa501428/hic-emt) for upgrading older .hic files or making smaller .hic files for regions of
interest.
--------------
About Juicebox
--------------
Juicebox is visualization software for Hi-C data. This distribution includes the source code for
Juicebox, Juicer Tools,
and Assembly Tools
. Download Juicebox here, or
use Juicebox on the web. Detailed documentation is
available on the wiki. Instructions below pertain primarily
to usage of command line tools and the Juicebox jar files.
Juicebox can now be used to visualize and interactively (re)assemble genomes. Check out the Juicebox Assembly Tools
Module website https://aidenlab.org/assembly for more details on how to use Juicebox for assembly.
Juicebox was created by Jim Robinson,
Neva C. Durand, and Erez Lieberman Aiden. Past contributors include Ido Machol, Zulkifl Gire, Marie Hoeger, Fanny Huang, Nam Hee Kim, Vi Nguyen, Jay Ryu, Nathaniel T. Musial, and Ragib Mostofa.
Ongoing development work is carried out by Muhammad Saad Shamim, Neva C. Durand, and Olga Dudchenko.
--------------
Questions?
--------------
For FAQs, or for asking new questions, please see our forum: aidenlab.org/forum.html.
--------------
IntelliJ Setup
--------------
Use IntelliJ IDEA (Community edition - free)
To set up in IDEA, have the Java SDK installed
then you'll point to it (IntelliJ has lots of documentation on this sort of thing).
* Then go to `VCS` -> `checkout from version control`.
* You'll need to do is be sure `*.sizes` is included as a file to be copied over to the class files.
Set this up via IntelliJ `Preferences` -> `Compiler`. Add `?*.sizes` to the list of `Resource Patterns`.
* While there, also go to `Java Compiler` and put this into additional command line options: `-Xlint:all -target 1.7`
The former turns on all warnings, the latter gives some flexibility since some people haven't updated Java to 1.8 yet.
* Then go to `Run` -> `Edit Configurations`.
* With the `+` sign, add `Application`.
* You'll create two of these, one for the GUI (call it Juicebox GUI or whatever you want, really) and one for the CLT.
* Set the main class by clicking the little `...` button next to the text box for main class
MainWindow.java is the main method class for the visualization/GUI portion of the software.
HiCTools.java is the main method class for the analysis/CLT portion.
* For the GUI under VM Options:
-Xmx2000m
-Djnlp.loadMenu="https://hicfiles.tc4ga.com/juicebox.properties"
* For the CLT use
-Xmx2000m
* Note that the `Xmx2000m` flag sets the maximum memory heap size to 2GB.
Depending on your computer you might want more or less.
Some tools will break if there's not enough memory and the file is too large,
but don't worry about that for development; 2GB should be fine.
* One last note: be sure to `Commit and Push` when you commit files, it's hidden in the dropdown menu button in the
commit window.
----------------------------------
Hardware and Software Requirements
----------------------------------
The minimum software requirement to run Juicebox is a working Java installation
(version > 1.6) on Windows, Linux, and Mac OSX. We recommend using the latest
Java version available, but please do not use the Java Beta Version. Minimum
system requirements for running Java can be found at
https://java.com/en/download/help/sysreq.xml. To download and install the latest
Java Runtime Environment (JRE), please go to https://www.java.com/download.
We recommend having at least 2GB free RAM for the best user experience with
Juicebox.
To launch the Juicebox application from command line, type java -Xms512m -Xmx2048m -jar Juicebox.jar
Note: the -Xms512m flag sets the minimum memory heap size at 512 megabytes, and
the -Xmx2048m flag sets the maximum size at 2048 megabytes (2 gigabytes). These
values may be adjusted as appropriate for your machine.
-------------
Documentation
-------------
We have extensive documentation for how to use Juicebox at
https://github.com/theaidenlab/juicebox/wiki including a video, a Quick Start Guide, and a
detailed tutorial.
For using Juicebox to assemble genomes see https://www.aidenlab.org/assembly/.
------------------------
Command Line Tools Usage
------------------------
See the documentation at https://github.com/theaidenlab/juicer/wiki for information
on how to use the Juicer tools.
--------------------------------
Compiling Jars from Source Files
--------------------------------
1. You should have Java 1.8 JDK and Apache Ant installed on your system. See below for more information.
2. Go to the folder containing the Juicebox source files and edit the juicebox.properties file with the proper Java JDK Address.
3. Open the command line, navigate to the folder containing the build.xml file and type ant The process should take no more than a minute to build on most machines.
4. The jars are written to the directory out/. You can change this by editing the build.xml file.
* Installing Java 1.8 JDK
For Windows/Mac/Linux, the Java 1.8 JDK can be installed from here:
https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
(Alternative) For Ubuntu/LinuxMint
https://tecadmin.net/install-oracle-java-8-jdk-8-ubuntu-via-ppa/
* Installing Apache Ant
Mac Ant should be installed on most Macs. To verify installation via the command prompt, type ant -version If Ant is not on your Mac, install it via homebrew. At the command prompt, type
brew update brew install ant You may need to install Homebrew (https://brew.sh/) on your machine See the following Stackoverflow post for more details:
https://stackoverflow.com/questions/3222804/how-can-i-install-apache-ant-on-mac-os-x
Windows Installing Ant requires some minor changes to your system environment. Follow the instructions in this article:
https://www.nczonline.net/blog/2012/04/12/how-to-install-apache-ant-on-windows/
Linux In the command prompt, type sudo apt-get install ant or sudo yum install ant depending on your package installer
================================================
FILE: benchmark.sh
================================================
#!/bin/bash
### Benchmarks for DCIC
### To run, get juicebox_tools.jar and the test data files:
### wget http://hicfiles.s3.amazonaws.com/internal/juicebox_tools/8.5.16/juicebox_tools.jar
### wget ftp://ftp.ncbi.nlm.nih.gov/geo/samples/GSM1551nnn/GSM1551552/suppl/GSM1551552_HIC003_merged_nodups.txt.gz
### wget https://s3.amazonaws.com/pkerp/data/matrix_test/chrX_5KB_bins.tsv.gz
### First benchmark
echo "Create index for data at 1MB resolution, all chromosomes"
time java -Xmx8g -jar juicebox_tools.jar pre -r 1000000 -v -q 1 GSM1551552_HIC003_merged_nodups.txt.gz HIC003_1MB.hic hg19
echo "Create index for data at all resolutions, all chromosomes"
time java -Xmx8g -jar juicebox_tools.jar pre -v -q 1 GSM1551552_HIC003_merged_nodups.txt.gz HIC003.hic hg19
echo "Querying: 256x256 / 2048x2048 / slices / single resolution no norm"
java -Xmx8g -jar juicebox_tools.jar benchmark HIC003_1MB.hic NONE
echo "Querying: 256x256 / 2048x2048 / slices / single resolution balanced norm"
java -Xmx8g -jar juicebox_tools.jar benchmark HIC003_1MB.hic KR
echo "Querying: 256x256 / 2048x2048 / slices / multiple resolution no norm"
java -Xmx8g -jar juicebox_tools.jar benchmark HIC003.hic NONE
echo "Querying: 256x256 / 2048x2048 / slices / multiple resolution balanced norm"
java -Xmx8g -jar juicebox_tools.jar benchmark HIC003.hic KR
# repeated query without loading index of 2048x2048
echo "Query without loading index 2048x2048....ten times"
time ./straw KR HIC003.hic 1:20480000:40960000 1:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 2:20480000:40960000 2:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 3:20480000:40960000 3:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 4:20480000:40960000 4:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 5:20480000:40960000 5:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 6:20480000:40960000 6:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 7:20480000:40960000 7:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 8:20480000:40960000 8:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 9:20480000:40960000 9:20480000:40960000 BP 10000 > tmp.txt
time ./straw KR HIC003.hic 10:20480000:40960000 10:20480000:40960000 BP 10000 > tmp.txt
### Second benchmark
#transform to valid-pairs format with scores
#gunzip -c chrX_5KB_bins.tsv.gz | awk '{print 0,"X",$1*5000,0,0,"X",$2*5000,1,$3}' | gzip > chrX_5KB.txt.gz
echo "Create index for binned data"
time java -Xmx8g -jar juicebox_tools.jar pre -r 5000 -c X -v -d chrX_5KB.txt.gz chrX_5KB.hic hg19
echo "Querying: 256x256 / 2048x2048 / slices / no norm"
java -Xmx8g -jar juicebox_tools.jar -v benchmark chrX_5KB.hic NONE X
echo "Querying: 256x256 / 2048x2048 / slices / balanced norm"
java -Xmx8g -jar juicebox_tools.jar -v benchmark chrX_5KB.hic KR X
echo "Binary index to text"
time java -Xmx8g -jar juicebox_tools.jar dump observed NONE chrX_5KB.hic X X BP 5000 chrX_5KB_out.txt
================================================
FILE: build.xml
================================================
================================================
FILE: data/inter.hic
================================================
[File too large to display: 40.0 MB]
================================================
FILE: internalREADME.md
================================================
--------------------
Quick Start
--------------------
1. Run `package.sh `
versionNum lives in src/juicebox/HiCGlobals.
This will compile, sign, bundle, and create the DMG and EXE.
You will be prompted to change the EXE with a HEX editor, just follow
the instructions.
2. The new executables will be in out/artifacts/Juicebox_jar
3. move old jars, EXEs, and DMGs, update CHANGES in important_jars
------------------
Previous version
------------------
```
rm -r out
ant
ant sign
ant bundle
cd l4j
~/Downloads/launch4j/launch4j config_bcm.xml
cd ../out/artifacts/Juicebox_jar/
codesign -s "Erez Aiden" Juicebox.app --deep
hdiutil create -srcfolder Juicebox.app Juicebox.dmg
cd ~/Dropbox\ \(Lab\ at\ Large\)/important_jars/
# move old jars, EXEs, and DMGs, update CHANGES
emacs -nw CHANGES
# XXXXXX should be date when file written i.e. 20170103
mv Juicebox_BCM.dmg Juicebox_BCMXXXXXXXX.dmg
mv Juicebox_BCM.exe Juicebox_BCMXXXXXXXX.exe
mv Juicebox.jar JuiceboxXXXXXXXX.jar
mv ~/Dropbox/Research/JuiceboxDev/l4j/Juicebox.exe Juicebox_BCM.exe
mv ~/Dropbox/Research/JuiceboxDev/out/artifacts/Juicebox_jar/Juicebox.dmg Juicebox_BCM.dmg
mv ~/Dropbox/Research/JuiceboxDev/out/artifacts/Juicebox_jar/Juicebox.jar Juicebox.jar
mv ~/Dropbox/Research/JuiceboxDev/out/artifacts/Juicebox_clt_jar/Juicebox.jar juicebox_tools.8.0.jar
```
--------------------
Creating Executables
--------------------
0. Compile Jars from Source Files as described in Juicebox README.
* EXE Build (Windows)
1. Download the launch4j tarball
and unzip it.
2. Run
> ./launch4j/launch4j config.xml
Modify the config.xml file to set the properties file and other parameters.
3. Signing is complicated. You'll need openssl and osslsigncode and will need to do a two step procedure.
- openssl pkcs12 -in ~/Dropbox\ \(Lab\ at\ Large\)/important_jars/ErezSLieberman.p12 -nocerts -nodes -out certificate.pem
(You can save this certificate and use it later, just don't forget your password)
- Sign for the first time
osslsigncode sign -certs ~/Dropbox\ \(Lab\ at\ Large\)/important_jars/erez_s_lieberman.pem -key certificate.pem \
-askpass -n "Juicebox" -i http://aidenlab.org/ -in ~/Dropbox\ \(Lab\ at\ Large\)/important_jars/Juicebox.exe \
-out signed.exe
- Find the size of the signature in bytes i.e. sizeInBytesOf(signed.exe) - sizeInBytesOf(Juicebox.exe)
You can use ls -l for this
- Edit Juicebox.exe with favorite HEX editor to change last two bytes of exe i.e. the jar i.e. the zip end of
central directory to the size using littleendian byte order and save. File size should remain the same.
For example, if the size difference is 4384, the hex number is 0x1120; in Little Endian, this will be 20 11
- Sign the modified Juicebox.exe using above osslsigncode again
* .app Build (Mac)
1. After making jars as described above, type
> ant bundle
to make the Juicebox.app executable.
2. Sign the app with codesign -s "Erez Aiden" Juicebox.app --deep
You must have things appropriately installed in your keychain. Follow the instructions on the Apple Developer website. Our csr and cer are in the Dropbox under important_jars but I'm not sure exactly how you would add both the csr and the cer to KeyChain so you might just have to do it from scratch (this is what I did, eventually). Look for the ones created Wed Dec 23 2015
3. hdiutil create -size 210m -srcfolder Juicebox.app Juicebox.dmg
Size depends on what the size of your .app file is, make it big enough so this command doesn't fail. You can also do Disk Utility -> New Image From Folder and choose Juicebox.app.
4. Modify the code under the bundle taskdef in the build.xml file to change properties and other parameters.
--------------------
Steps for creating Public Friendly Version
--------------------
1. Create a new branch
2. Remove sensitive chrom.sizes. First, delete all contents of the chrom.sizes folder (/src/juicebox/tools/chrom.sizes). Next, go to the top level directory. You will see a compressed folder called PublicFriendlyChromSizes.zip. Extract its contents and copy them into the chrom.sizes folder which was just emptied. Recompile the project in IntelliJ.
3. Delete anything under development (e.g. clustering, APAvsDistance, etc.). These should all be in the dev folder, so it should usually be sufficient to just delete it and all its contents (src/juicebox/tools/dev). After deleting the folder, compile in IntelliJ and fix all the bugs/warnings (i.e. remove any calls to the dev/private folder). This may be tricky for some parts (especially the restriction enzymes section) See https://github.com/theaidenlab/JuiceboxDev/commit/fd930f5fac9af3df9f44cd87d4fc31e8df5d3ac3 for an example of what was deleted. (Aside: Any new sensitive project should be created in this dev directory to simplify this entire process for us.)
4. Remove any mention of assembly/sensitive projects (should have technically been taken care on in step 3, but a quick search for the word assembly in the whole project is easy to do)
5. Change version number as appropriate - HiCGlobals, for display purposes only.
Note this is only in terms of jars/executables.
For actual code release / open-sourcing, we need to wipe other private files, especially the .git histories, hidden files, internalREADME (i.e. me!), etc.
--------------------
Building a new IGV jar for use in Juicebox
-------------------
Two problems with IGV jar: signatures and classpaths in the MANIFEST. If it was just the first, it would be a one-liner. Instead:
1 - Unzip IGV jar (be sure to get the "snapshot" build, currently at https://data.broadinstitute.org/igv/projects/snapshot/igv.jar )
```
mkdir tmp
mv igv.jar tmp
cd tmp
unzip igv.jar
```
2 - Remove META-INF/*.SF META-INF/*.DSA META-INF/*.RSA
```
rm META-INF/*.SF META-INF/*.DSA META-INF/*.RSA
```
3 - Go into the META-INF/MANIFEST file with your favorite editor and remove the Class-Path lines.
4 - Rezip the jar
```
rm igv.jar
jar cvf igv.jar ./*
```
--------------------
Building Different CUDA versions
--------------------
Right now JuiceboxDev (assuming what's in the repo now) is defaulting to 7.0
CUDA 7.0 is used on AWS
CUDA 7.5 is on most of our internal machines (Hailmary)
CUDA 8.0 is used on Adam; linux-x64
CUDA 8.0 is used on Rice; linux-ppc
CUDA 8.0 for Mac; apple-x86
To change from 7.0 to 7.5, the build.xml and
libraries for compilation need to be changed
---build.xml changes
Change the following line (has x2 occurrences, one for each jar build)
``
to
``
or
``
and for Power (Rice) will need
``
and otherwise
``
---jcuda lib changes
- Go to ~/lib/jcuda
- Delete all the non .zip files
- Unzip Archive.JCuda.0.7.5.zip
this creates a new folder
- Move everything in this newly made folder to the folder above it
(e.g. path should be ~/lib/jcuda/jcuda-0.7.5.jar, NOT ~/lib/jcuda/Archive.JCuda.0.7.5/jcuda-0.7.0.jar)
- For JCuda 0.8, you should define if this is for x86 or ppc (Power8) architecture and add those natives: jcuda-natives-0.8.0-linux-ppc_64.jar or jcuda-natives-0.8.0-linux-x86_64.jar
The jars are really the ones that matter, but I think it's easier to update everything here (especially if testing GPU stuff from within IntelliJ)
And now it should be fine to build via ant
================================================
FILE: juicebox.properties
================================================
#
# The MIT License (MIT)
#
# Copyright (c) 2011-2020 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
jdk.home.1.8=/Library/Java/JavaVirtualMachines/jdk-13.0.2.jdk/Contents/Home/
sign.keystore=/Users/nchernia/Dropbox\ \(Lab\ at\ Large\)/important_jars//ErezSLieberman.jks
sign.storepass=juicebox
sign.alias=server
================================================
FILE: l4j/config.xml
================================================
gui../out/artifacts/Juicebox_jar/Juicebox.jarJuicebox.exeJuicebox.Juicebox.icotrue1.7.064500010000juicebox.bmptrue60true
================================================
FILE: l4j/config_bcm.xml
================================================
gui../out/artifacts/Juicebox_jar/Juicebox.jarJuicebox.exeJuicebox.Juicebox.icotrue1.7.064500010000-Djnlp.loadMenu="http://hicfiles.s3.amazonaws.com/internal/hiculfite.properties"juicebox.bmptrue60true
================================================
FILE: l4j/config_broad.xml
================================================
gui../out/artifacts/Juicebox_jar/juicebox_64.jar../Juicebox.exeJuicebox.Juicebox.icotrue1.7.064500010000-Djnlp.loadMenu="http://iwww.broadinstitute.org/igvdata/hic/files/hicInternalMenu.properties"juicebox.bmptrue60true
================================================
FILE: l4j/config_fix_for_Erez.xml
================================================
gui../out/artifacts/Juicebox_jar/Juicebox.jar../Juicebox.exeJuicebox.Juicebox.icotrue1.7.064500010000-Dswing.aatext=true-Dswing.plaf.metal.controlFont=Tahoma-Dswing.plaf.metal.userFont=Tahomajuicebox.bmptrue60true
================================================
FILE: l4j/config_nosplash.xml
================================================
gui../out/artifacts/Juicebox_jar/Juicebox.jar../out/artifacts/Juicebox_jar/Juicebox.exeJuicebox.Juicebox.icotrue1.7.064500010000
================================================
FILE: lib/broadinstitute/igv.jar
================================================
[File too large to display: 19.3 MB]
================================================
FILE: lib/general/VectorGraphics2D.LGPL.LICENSE
================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
================================================
FILE: lib/general/hic_tools.3.30.00.jar
================================================
[File too large to display: 24.7 MB]
================================================
FILE: lib/universalJavaApplicationStub
================================================
#!/bin/bash
##################################################################################
# #
# universalJavaApplicationStub #
# #
# A BASH based JavaApplicationStub for Java Apps on Mac OS X #
# that works with both Apple's and Oracle's plist format. #
# #
# Inspired by Ian Roberts stackoverflow answer #
# at http://stackoverflow.com/a/17546508/1128689 #
# #
# @author Tobias Fischer #
# @url https://github.com/tofi86/universalJavaApplicationStub #
# @date 2018-03-10 #
# @version 3.0.1 #
# #
##################################################################################
# #
# The MIT License (MIT) #
# #
# Copyright (c) 2014-2018 Tobias Fischer #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy #
# of this software and associated documentation files (the "Software"), to deal #
# in the Software without restriction, including without limitation the rights #
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #
# copies of the Software, and to permit persons to whom the Software is #
# furnished to do so, subject to the following conditions: #
# #
# The above copyright notice and this permission notice shall be included in all #
# copies or substantial portions of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #
# SOFTWARE. #
# #
##################################################################################
# function 'stub_logger()'
#
# A logger which logs to the macOS Console.app using the 'syslog' command
#
# @param1 the log message
# @return void
################################################################################
function stub_logger() {
syslog -s -k \
Facility com.apple.console \
Level Notice \
Sender "$(basename "$0")" \
Message "[$$][${CFBundleName:-$(basename "$0")}] $1"
}
# set the directory abspath of the current
# shell script with symlinks being resolved
############################################
PRG=$0
while [ -h "$PRG" ]; do
ls=$(ls -ld "$PRG")
link=$(expr "$ls" : '^.*-> \(.*\)$' 2>/dev/null)
if expr "$link" : '^/' 2> /dev/null >/dev/null; then
PRG="$link"
else
PRG="$(dirname "$PRG")/$link"
fi
done
PROGDIR=$(dirname "$PRG")
stub_logger "[StubDir] $PROGDIR"
# set files and folders
############################################
# the absolute path of the app package
cd "$PROGDIR"/../../ || exit 11
AppPackageFolder=$(pwd)
# the base path of the app package
cd .. || exit 12
AppPackageRoot=$(pwd)
# set Apple's Java folder
AppleJavaFolder="${AppPackageFolder}"/Contents/Resources/Java
# set Apple's Resources folder
AppleResourcesFolder="${AppPackageFolder}"/Contents/Resources
# set Oracle's Java folder
OracleJavaFolder="${AppPackageFolder}"/Contents/Java
# set Oracle's Resources folder
OracleResourcesFolder="${AppPackageFolder}"/Contents/Resources
# set path to Info.plist in bundle
InfoPlistFile="${AppPackageFolder}"/Contents/Info.plist
# set the default JVM Version to a null string
JVMVersion=""
JVMMaxVersion=""
# function 'plist_get()'
#
# read a specific Plist key with 'PlistBuddy' utility
#
# @param1 the Plist key with leading colon ':'
# @return the value as String or Array
################################################################################
plist_get(){
/usr/libexec/PlistBuddy -c "print $1" "${InfoPlistFile}" 2> /dev/null
}
# function 'plist_get_java()'
#
# read a specific Plist key with 'PlistBuddy' utility
# in the 'Java' or 'JavaX' dictionary ()
#
# @param1 the Plist :Java(X):Key with leading colon ':'
# @return the value as String or Array
################################################################################
plist_get_java(){
plist_get ${JavaKey:-":Java"}$1
}
# read Info.plist and extract JVM options
############################################
# read the program name from CFBundleName
CFBundleName=$(plist_get ':CFBundleName')
# read the icon file name
CFBundleIconFile=$(plist_get ':CFBundleIconFile')
# check Info.plist for Apple style Java keys -> if key :Java is present, parse in apple mode
/usr/libexec/PlistBuddy -c "print :Java" "${InfoPlistFile}" > /dev/null 2>&1
exitcode=$?
JavaKey=":Java"
# if no :Java key is present, check Info.plist for universalJavaApplication style JavaX keys -> if key :JavaX is present, parse in apple mode
if [ $exitcode -ne 0 ]; then
/usr/libexec/PlistBuddy -c "print :JavaX" "${InfoPlistFile}" > /dev/null 2>&1
exitcode=$?
JavaKey=":JavaX"
fi
# read 'Info.plist' file in Apple style if exit code returns 0 (true, ':Java' key is present)
if [ $exitcode -eq 0 ]; then
stub_logger "[PlistStyle] Apple"
# set Java and Resources folder
JavaFolder="${AppleJavaFolder}"
ResourcesFolder="${AppleResourcesFolder}"
APP_PACKAGE="${AppPackageFolder}"
JAVAROOT="${AppleJavaFolder}"
USER_HOME="$HOME"
# read the Java WorkingDirectory
JVMWorkDir=$(plist_get_java ':WorkingDirectory' | xargs)
# set Working Directory based upon PList value
if [[ ! -z ${JVMWorkDir} ]]; then
WorkingDirectory="${JVMWorkDir}"
else
# AppPackageRoot is the standard WorkingDirectory when the script is started
WorkingDirectory="${AppPackageRoot}"
fi
# expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
WorkingDirectory=$(eval echo "${WorkingDirectory}")
# read the MainClass name
JVMMainClass="$(plist_get_java ':MainClass')"
# read the SplashFile name
JVMSplashFile=$(plist_get_java ':SplashFile')
# read the JVM Properties as an array and retain spaces
IFS=$'\t\n'
JVMOptions=($(xargs -n1 <<<$(plist_get_java ':Properties' | grep " =" | sed 's/^ */-D/g' | sed -E 's/ = (.*)$/="\1"/g')))
unset IFS
# post processing of the array follows further below...
# read the ClassPath in either Array or String style
JVMClassPath_RAW=$(plist_get_java ':ClassPath' | xargs)
if [[ $JVMClassPath_RAW == *Array* ]] ; then
JVMClassPath=.$(plist_get_java ':ClassPath' | grep " " | sed 's/^ */:/g' | tr -d '\n' | xargs)
else
JVMClassPath=${JVMClassPath_RAW}
fi
# expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
# read the JVM Options in either Array or String style
JVMDefaultOptions_RAW=$(plist_get_java ':VMOptions' | xargs)
if [[ $JVMDefaultOptions_RAW == *Array* ]] ; then
JVMDefaultOptions=$(plist_get_java ':VMOptions' | grep " " | sed 's/^ */ /g' | tr -d '\n' | xargs)
else
JVMDefaultOptions=${JVMDefaultOptions_RAW}
fi
# read StartOnMainThread and add as -XstartOnFirstThread
JVMStartOnMainThread=$(plist_get_java ':StartOnMainThread')
if [ "${JVMStartOnMainThread}" == "true" ]; then
JVMDefaultOptions+=" -XstartOnFirstThread"
fi
# read the JVM Arguments as an array and retain spaces
IFS=$'\t\n'
MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments')))
unset IFS
# post processing of the array follows further below...
# read the Java version we want to find
JVMVersion=$(plist_get_java ':JVMVersion' | xargs)
# post processing of the version string follows below...
# read 'Info.plist' file in Oracle style
else
stub_logger "[PlistStyle] Oracle"
# set Working Directory and Java and Resources folder
JavaFolder="${OracleJavaFolder}"
ResourcesFolder="${OracleResourcesFolder}"
WorkingDirectory="${OracleJavaFolder}"
APP_ROOT="${AppPackageFolder}"
# read the MainClass name
JVMMainClass="$(plist_get ':JVMMainClassName')"
# read the SplashFile name
JVMSplashFile=$(plist_get ':JVMSplashFile')
# read the JVM Options as an array and retain spaces
IFS=$'\t\n'
JVMOptions=($(plist_get ':JVMOptions' | grep " " | sed 's/^ *//g'))
unset IFS
# post processing of the array follows further below...
# read the ClassPath in either Array or String style
JVMClassPath_RAW=$(plist_get ':JVMClassPath')
if [[ $JVMClassPath_RAW == *Array* ]] ; then
JVMClassPath=.$(plist_get ':JVMClassPath' | grep " " | sed 's/^ */:/g' | tr -d '\n' | xargs)
# expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
elif [[ ! -z ${JVMClassPath_RAW} ]] ; then
JVMClassPath=${JVMClassPath_RAW}
# expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
else
#default: fallback to OracleJavaFolder
JVMClassPath="${JavaFolder}/*"
# Do NOT expand the default 'AppName.app/Contents/Java/*' classpath (#42)
fi
# read the JVM Default Options
JVMDefaultOptions=$(plist_get ':JVMDefaultOptions' | grep -o " \-.*" | tr -d '\n' | xargs)
# read the Main Arguments from JVMArguments key as an array and retain spaces (see #46 for naming details)
IFS=$'\t\n'
MainArgs=($(xargs -n1 <<<$(plist_get ':JVMArguments' | tr -d '\n' | sed -E 's/Array \{ *(.*) *\}/\1/g' | sed 's/ */ /g')))
unset IFS
# post processing of the array follows further below...
# read the Java version we want to find
JVMVersion=$(plist_get ':JVMVersion' | xargs)
# post processing of the version string follows below...
fi
# JVMVersion: post processing and optional splitting
if [[ ${JVMVersion} == *";"* ]]; then
minMaxArray=(${JVMVersion//;/ })
JVMVersion=${minMaxArray[0]//+}
JVMMaxVersion=${minMaxArray[1]//+}
fi
stub_logger "[JavaRequirement] JVM minimum version: ${JVMVersion}"
stub_logger "[JavaRequirement] JVM maximum version: ${JVMMaxVersion}"
# MainArgs: replace occurences of $APP_ROOT with its content
MainArgsArr=()
for i in "${MainArgs[@]}"
do
MainArgsArr+=("$(eval echo "$i")")
done
# JVMOptions: replace occurences of $APP_ROOT with its content
JVMOptionsArr=()
for i in "${JVMOptions[@]}"
do
JVMOptionsArr+=("$(eval echo "$i")")
done
# internationalized messages
############################################
LANG=$(defaults read -g AppleLocale)
stub_logger "[Language] $LANG"
# French localization
if [[ $LANG == fr* ]] ; then
MSG_ERROR_LAUNCHING="ERREUR au lancement de '${CFBundleName}'."
MSG_MISSING_MAINCLASS="'MainClass' n'est pas spécifié.\nL'application Java ne peut pas être lancée."
MSG_JVMVERSION_REQ_INVALID="La syntaxe de la version Java demandée est invalide: %s\nVeuillez contacter le développeur de l'application."
MSG_NO_SUITABLE_JAVA="La version de Java installée sur votre système ne convient pas.\nCe programme nécessite Java %s"
MSG_JAVA_VERSION_OR_LATER="ou ultérieur"
MSG_JAVA_VERSION_LATEST="(dernière mise à jour)"
MSG_JAVA_VERSION_MAX="à %s"
MSG_NO_SUITABLE_JAVA_CHECK="Merci de bien vouloir installer la version de Java requise."
MSG_INSTALL_JAVA="Java doit être installé sur votre système.\nRendez-vous sur java.com et suivez les instructions d'installation..."
MSG_LATER="Plus tard"
MSG_VISIT_JAVA_DOT_COM="Visiter java.com"
# German localization
elif [[ $LANG == de* ]] ; then
MSG_ERROR_LAUNCHING="FEHLER beim Starten von '${CFBundleName}'."
MSG_MISSING_MAINCLASS="Die 'MainClass' ist nicht spezifiziert!\nDie Java-Anwendung kann nicht gestartet werden!"
MSG_JVMVERSION_REQ_INVALID="Die Syntax der angeforderten Java-Version ist ungültig: %s\nBitte kontaktieren Sie den Entwickler der App."
MSG_NO_SUITABLE_JAVA="Es wurde keine passende Java-Version auf Ihrem System gefunden!\nDieses Programm benötigt Java %s"
MSG_JAVA_VERSION_OR_LATER="oder neuer"
MSG_JAVA_VERSION_LATEST="(neuste Unterversion)"
MSG_JAVA_VERSION_MAX="bis %s"
MSG_NO_SUITABLE_JAVA_CHECK="Stellen Sie sicher, dass die angeforderte Java-Version installiert ist."
MSG_INSTALL_JAVA="Auf Ihrem System muss die 'Java'-Software installiert sein.\nBesuchen Sie java.com für weitere Installationshinweise."
MSG_LATER="Später"
MSG_VISIT_JAVA_DOT_COM="java.com öffnen"
# Simplifyed Chinese localization
elif [[ $LANG == zh* ]] ; then
MSG_ERROR_LAUNCHING="无法启动 '${CFBundleName}'."
MSG_MISSING_MAINCLASS="没有指定 'MainClass'!\nJava程序无法启动!"
MSG_JVMVERSION_REQ_INVALID="Java版本参数语法错误: %s\n请联系该应用的开发者。"
MSG_NO_SUITABLE_JAVA="没有在系统中找到合适的Java版本!\n必须安装Java %s才能够使用该程序!"
MSG_JAVA_VERSION_OR_LATER="及以上版本"
MSG_JAVA_VERSION_LATEST="(最新版本)"
MSG_JAVA_VERSION_MAX="最高为 %s"
MSG_NO_SUITABLE_JAVA_CHECK="请确保系统中安装了所需的Java版本"
MSG_INSTALL_JAVA="你需要在Mac中安装Java运行环境!\n访问 java.com 了解如何安装。"
MSG_LATER="稍后"
MSG_VISIT_JAVA_DOT_COM="访问 java.com"
# English default localization
else
MSG_ERROR_LAUNCHING="ERROR launching '${CFBundleName}'."
MSG_MISSING_MAINCLASS="'MainClass' isn't specified!\nJava application cannot be started!"
MSG_JVMVERSION_REQ_INVALID="The syntax of the required Java version is invalid: %s\nPlease contact the App developer."
MSG_NO_SUITABLE_JAVA="No suitable Java version found on your system!\nThis program requires Java %s"
MSG_JAVA_VERSION_OR_LATER="or later"
MSG_JAVA_VERSION_LATEST="(latest update)"
MSG_JAVA_VERSION_MAX="up to %s"
MSG_NO_SUITABLE_JAVA_CHECK="Make sure you install the required Java version."
MSG_INSTALL_JAVA="You need to have JAVA installed on your Mac!\nVisit java.com for installation instructions..."
MSG_LATER="Later"
MSG_VISIT_JAVA_DOT_COM="Visit java.com"
fi
# function 'get_java_version_from_cmd()'
#
# returns Java version string from 'java -version' command
# works for both old (1.8) and new (9) version schema
#
# @param1 path to a java JVM executable
# @return the Java version number as displayed in 'java -version' command
################################################################################
function get_java_version_from_cmd() {
# second sed command strips " and -ea from the version string
echo $("$1" -version 2>&1 | awk '/version/{print $NF}' | sed -E 's/"//g;s/-ea//g')
}
# function 'extract_java_major_version()'
#
# extract Java major version from a version string
#
# @param1 a Java version number ('1.8.0_45') or requirement string ('1.8+')
# @return the major version (e.g. '7', '8' or '9', etc.)
################################################################################
function extract_java_major_version() {
echo $(echo "$1" | sed -E 's/^1\.//;s/^([0-9]+)(-ea|(\.[0-9_.]{1,7})?)(-b[0-9]+-[0-9]+)?[+*]?$/\1/')
}
# function 'get_comparable_java_version()'
#
# return comparable version for a Java version number or requirement string
#
# @param1 a Java version number ('1.8.0_45') or requirement string ('1.8+')
# @return an 8 digit numeral ('1.8.0_45'->'08000045'; '9.1.13'->'09001013')
################################################################################
function get_comparable_java_version() {
# cleaning: 1) remove leading '1.'; 2) remove build string (e.g. '-b14-468'); 3) remove 'a-Z' and '-*+' (e.g. '-ea'); 4) replace '_' with '.'
local cleaned=$(echo "$1" | sed -E 's/^1\.//g;s/-b[0-9]+-[0-9]+$//g;s/[a-zA-Z+*\-]//g;s/_/./g')
# splitting at '.' into an array
local arr=( ${cleaned//./ } )
# echo a string with left padded version numbers
echo "$(printf '%02s' ${arr[0]})$(printf '%03s' ${arr[1]})$(printf '%03s' ${arr[2]})"
}
# function 'is_valid_requirement_pattern()'
#
# check whether the Java requirement is a valid requirement pattern
#
# supported requirements are for example:
# - 1.6 requires Java 6 (any update) [1.6, 1.6.0_45, 1.6.0_88]
# - 1.6* requires Java 6 (any update) [1.6, 1.6.0_45, 1.6.0_88]
# - 1.6+ requires Java 6 or higher [1.6, 1.6.0_45, 1.8, 9, etc.]
# - 1.6.0 requires Java 6 (any update) [1.6, 1.6.0_45, 1.6.0_88]
# - 1.6.0_45 requires Java 6u45 [1.6.0_45]
# - 1.6.0_45+ requires Java 6u45 or higher [1.6.0_45, 1.6.0_88, 1.8, etc.]
# - 9 requires Java 9 (any update) [9.0.*, 9.1, 9.3, etc.]
# - 9* requires Java 9 (any update) [9.0.*, 9.1, 9.3, etc.]
# - 9+ requires Java 9 or higher [9.0, 9.1, 10, etc.]
# - 9.1 requires Java 9.1 (any update) [9.1.*, 9.1.2, 9.1.13, etc.]
# - 9.1* requires Java 9.1 (any update) [9.1.*, 9.1.2, 9.1.13, etc.]
# - 9.1+ requires Java 9.1 or higher [9.1, 9.2, 10, etc.]
# - 9.1.3 requires Java 9.1.3 [9.1.3]
# - 9.1.3* requires Java 9.1.3 (any update) [9.1.3]
# - 9.1.3+ requires Java 9.1.3 or higher [9.1.3, 9.1.4, 9.2.*, 10, etc.]
# - 10-ea requires Java 10 (early access release)
#
# unsupported requirement patterns are for example:
# - 1.2, 1.3, 1.9 Java 2, 3 are not supported
# - 1.9 Java 9 introduced a new versioning scheme
# - 6u45 known versioning syntax, but unsupported
# - 9-ea*, 9-ea+ early access releases paired with */+
# - 9., 9.*, 9.+ version ending with a .
# - 9.1., 9.1.*, 9.1.+ version ending with a .
# - 9.3.5.6 4 part version number is unsupported
#
# @param1 a Java requirement string ('1.8+')
# @return boolean exit code: 0 (is valid), 1 (is not valid)
################################################################################
function is_valid_requirement_pattern() {
local java_req=$1
java8pattern='1\.[4-8](\.0)?(\.0_[0-9]+)?[*+]?'
java9pattern='(9|1[0-9])(-ea|[*+]|(\.[0-9]+){1,2}[*+]?)?'
# test matches either old Java versioning scheme (up to 1.8) or new scheme (starting with 9)
if [[ ${java_req} =~ ^(${java8pattern}|${java9pattern})$ ]]; then
return 0
else
return 1
fi
}
# determine which JVM to use
############################################
# default Apple JRE plugin path (< 1.6)
apple_jre_plugin="/Library/Java/Home/bin/java"
apple_jre_version=$(get_java_version_from_cmd "${apple_jre_plugin}")
# default Oracle JRE plugin path (>= 1.7)
oracle_jre_plugin="/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"
oracle_jre_version=$(get_java_version_from_cmd "${oracle_jre_plugin}")
# first check system variable "$JAVA_HOME" -> has precedence over any other System JVM
stub_logger '[JavaSearch] Checking for $JAVA_HOME ...'
if [ -n "$JAVA_HOME" ] ; then
stub_logger "[JavaSearch] ... found JAVA_HOME with value $JAVA_HOME"
# PR 26: Allow specifying "$JAVA_HOME" relative to "$AppPackageFolder"
# which allows for bundling a custom version of Java inside your app!
if [[ $JAVA_HOME == /* ]] ; then
# if "$JAVA_HOME" starts with a Slash it's an absolute path
JAVACMD="$JAVA_HOME/bin/java"
else
# otherwise it's a relative path to "$AppPackageFolder"
JAVACMD="$AppPackageFolder/$JAVA_HOME/bin/java"
fi
JAVACMD_version=$(get_comparable_java_version $(get_java_version_from_cmd "${JAVACMD}"))
else
stub_logger "[JavaSearch] ... didn't found JAVA_HOME"
fi
# check for any other or a specific Java version
# also if $JAVA_HOME exists but isn't executable
if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
stub_logger "[JavaSearch] Checking for JavaVirtualMachines on the system ..."
# reset variables
JAVACMD=""
JAVACMD_version=""
# first check whether JVMVersion string is a valid requirement string
if [ ! -z "${JVMVersion}" ] && ! is_valid_requirement_pattern ${JVMVersion} ; then
MSG_JVMVERSION_REQ_INVALID_EXPANDED=$(printf "${MSG_JVMVERSION_REQ_INVALID}" "${JVMVersion}")
# log exit cause
stub_logger "[EXIT 4] ${MSG_JVMVERSION_REQ_INVALID_EXPANDED}"
# display error message with AppleScript
osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
# exit with error
exit 4
fi
# then check whether JVMMaxVersion string is a valid requirement string
if [ ! -z "${JVMMaxVersion}" ] && ! is_valid_requirement_pattern ${JVMMaxVersion} ; then
MSG_JVMVERSION_REQ_INVALID_EXPANDED=$(printf "${MSG_JVMVERSION_REQ_INVALID}" "${JVMMaxVersion}")
# log exit cause
stub_logger "[EXIT 5] ${MSG_JVMVERSION_REQ_INVALID_EXPANDED}"
# display error message with AppleScript
osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
# exit with error
exit 5
fi
# find installed JavaVirtualMachines (JDK + JRE)
allJVMs=()
# read JDK's from '/usr/libexec/java_home -V' command
while read -r line; do
version=$(echo $line | awk -F $',' '{print $1;}')
path=$(echo $line | awk -F $'" ' '{print $2;}')
path+="/bin/java"
allJVMs+=("$version:$path")
done < <(/usr/libexec/java_home -V 2>&1 | grep '^[[:space:]]')
# unset while loop variables
unset version path
# add Apple JRE if available
if [ -x "${apple_jre_plugin}" ] ; then
allJVMs+=("$apple_jre_version:$apple_jre_plugin")
fi
# add Oracle JRE if available
if [ -x "${oracle_jre_plugin}" ] ; then
allJVMs+=("$oracle_jre_version:$oracle_jre_plugin")
fi
# debug output
for i in "${allJVMs[@]}"
do
stub_logger "[JavaSearch] ... found JVM: $i"
done
# determine JVMs matching the min/max version requirement
minC=$(get_comparable_java_version ${JVMVersion})
maxC=$(get_comparable_java_version ${JVMMaxVersion})
matchingJVMs=()
for i in "${allJVMs[@]}"
do
# split JVM string at ':' delimiter to retain spaces in $path substring
IFS=: arr=($i) ; unset IFS
# [0] JVM version number
ver=${arr[0]}
# comparable JVM version number
comp=$(get_comparable_java_version $ver)
# [1] JVM path
path="${arr[1]}"
# construct string item for adding to the "matchingJVMs" array
item="$comp:$ver:$path"
# pre-requisite: current version number needs to be greater than min version number
if [ "$comp" -ge "$minC" ] ; then
# perform max version checks if max version requirement is present
if [ ! -z ${JVMMaxVersion} ] ; then
# max version requirement ends with '*' modifier
if [[ ${JVMMaxVersion} == *\* ]] ; then
# use the '*' modifier from the max version string as wildcard for a 'starts with' comparison
# and check whether the current version number starts with the max version wildcard string
if [[ ${ver} == ${JVMMaxVersion} ]]; then
matchingJVMs+=("$item")
# or whether the current comparable version is lower than the comparable max version
elif [ "$comp" -le "$maxC" ] ; then
matchingJVMs+=("$item")
fi
# max version requirement ends with '+' modifier -> always add this version if it's greater than $min
# because a max requirement with + modifier doesn't make sense
elif [[ ${JVMMaxVersion} == *+ ]] ; then
matchingJVMs+=("$item")
# matches 6 zeros at the end of the max version string (e.g. for 1.8, 9)
# -> then the max version string should be treated like with a '*' modifier at the end
#elif [[ ${maxC} =~ ^[0-9]{2}0{6}$ ]] && [ "$comp" -le $(( ${maxC#0} + 999 )) ] ; then
# matchingJVMs+=("$item")
# matches 3 zeros at the end of the max version string (e.g. for 9.1, 10.3)
# -> then the max version string should be treated like with a '*' modifier at the end
#elif [[ ${maxC} =~ ^[0-9]{5}0{3}$ ]] && [ "$comp" -le "${maxC}" ] ; then
# matchingJVMs+=("$item")
# matches standard requirements without modifier
elif [ "$comp" -le "$maxC" ]; then
matchingJVMs+=("$item")
fi
# no max version requirement:
# min version requirement ends with '+' modifier
# -> always add the current version because it's greater than $min
elif [[ ${JVMVersion} == *+ ]] ; then
matchingJVMs+=("$item")
# min version requirement ends with '*' modifier
# -> use the '*' modifier from the min version string as wildcard for a 'starts with' comparison
# and check whether the current version number starts with the min version wildcard string
elif [[ ${JVMVersion} == *\* ]] ; then
if [[ ${ver} == ${JVMVersion} ]] ; then
matchingJVMs+=("$item")
fi
# compare the min version against the current version with an additional * wildcard for a 'starts with' comparison
# -> e.g. add 1.8.0_44 when the requirement is 1.8
elif [[ ${ver} == ${JVMVersion}* ]] ; then
matchingJVMs+=("$item")
fi
fi
done
# unset for loop variables
unset arr ver comp path item
# debug output
for i in "${matchingJVMs[@]}"
do
stub_logger "[JavaSearch] ... ... matches all requirements: $i"
done
# sort the matching JavaVirtualMachines by version number
# https://stackoverflow.com/a/11789688/1128689
IFS=$'\n' matchingJVMs=($(sort -nr <<<"${matchingJVMs[*]}"))
unset IFS
# get the highest matching JVM
for ((i = 0; i < ${#matchingJVMs[@]}; i++));
do
# split JVM string at ':' delimiter to retain spaces in $path substring
IFS=: arr=(${matchingJVMs[$i]}) ; unset IFS
# [0] comparable JVM version number
comp=${arr[0]}
# [1] JVM version number
ver=${arr[1]}
# [2] JVM path
path="${arr[2]}"
# use current value as JAVACMD if it's executable
if [ -x "$path" ] ; then
JAVACMD="$path"
JAVACMD_version=$comp
break
fi
done
# unset for loop variables
unset arr comp ver path
fi
# log the Java Command and the extracted version number
stub_logger "[JavaCommand] '$JAVACMD'"
stub_logger "[JavaVersion] $(get_java_version_from_cmd "${JAVACMD}")${JAVACMD_version:+ / $JAVACMD_version}"
if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
# different error messages when a specific JVM was required
if [ ! -z "${JVMVersion}" ] ; then
# display human readable java version (#28)
java_version_hr=$(echo ${JVMVersion} | sed -E 's/^1\.([0-9+*]+)$/ \1/g' | sed "s/+/ ${MSG_JAVA_VERSION_OR_LATER}/;s/*/ ${MSG_JAVA_VERSION_LATEST}/")
MSG_NO_SUITABLE_JAVA_EXPANDED=$(printf "${MSG_NO_SUITABLE_JAVA}" "${java_version_hr}").
if [ ! -z "${JVMMaxVersion}" ] ; then
java_version_hr=$(extract_java_major_version ${JVMVersion})
java_version_max_hr=$(echo ${JVMMaxVersion} | sed -E 's/^1\.([0-9+*]+)$/ \1/g' | sed "s/+//;s/*/ ${MSG_JAVA_VERSION_LATEST}/")
MSG_NO_SUITABLE_JAVA_EXPANDED="$(printf "${MSG_NO_SUITABLE_JAVA}" "${java_version_hr}") $(printf "${MSG_JAVA_VERSION_MAX}" "${java_version_max_hr}")"
fi
# log exit cause
stub_logger "[EXIT 3] ${MSG_NO_SUITABLE_JAVA_EXPANDED}"
# display error message with AppleScript
osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_NO_SUITABLE_JAVA_EXPANDED}\n${MSG_NO_SUITABLE_JAVA_CHECK}\" with title \"${CFBundleName}\" buttons {\" OK \", \"${MSG_VISIT_JAVA_DOT_COM}\"} default button \"${MSG_VISIT_JAVA_DOT_COM}\" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)" \
-e "set response to button returned of the result" \
-e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"http://java.com\""
# exit with error
exit 3
else
# log exit cause
stub_logger "[EXIT 1] ${MSG_ERROR_LAUNCHING}"
# display error message with AppleScript
osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_INSTALL_JAVA}\" with title \"${CFBundleName}\" buttons {\"${MSG_LATER}\", \"${MSG_VISIT_JAVA_DOT_COM}\"} default button \"${MSG_VISIT_JAVA_DOT_COM}\" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)" \
-e "set response to button returned of the result" \
-e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"http://java.com\""
# exit with error
exit 1
fi
fi
# MainClass check
############################################
if [ -z "${JVMMainClass}" ]; then
# log exit cause
stub_logger "[EXIT 2] ${MSG_MISSING_MAINCLASS}"
# display error message with AppleScript
osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_MISSING_MAINCLASS}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
# exit with error
exit 2
fi
# execute $JAVACMD and do some preparation
############################################
# enable drag&drop to the dock icon
export CFProcessPath="$0"
# remove Apples ProcessSerialNumber from passthru arguments (#39)
if [[ $@ == -psn* ]] ; then
ArgsPassthru=()
else
ArgsPassthru=("$@")
fi
# change to Working Directory based upon Apple/Oracle Plist info
cd "${WorkingDirectory}" || exit 13
stub_logger "[WorkingDirectory] ${WorkingDirectory}"
# execute Java and set
# - classpath
# - splash image
# - dock icon
# - app name
# - JVM options
# - JVM default options
# - main class
# - main arguments
# - passthru arguments
stub_logger "[Exec] \"$JAVACMD\" -cp \"${JVMClassPath}\" -splash:\"${ResourcesFolder}/${JVMSplashFile}\" -Xdock:icon=\"${ResourcesFolder}/${CFBundleIconFile}\" -Xdock:name=\"${CFBundleName}\" ${JVMOptionsArr:+$(printf "'%s' " "${JVMOptionsArr[@]}") }${JVMDefaultOptions:+$JVMDefaultOptions }${JVMMainClass}${MainArgsArr:+ $(printf "'%s' " "${MainArgsArr[@]}")}${ArgsPassthru:+ $(printf "'%s' " "${ArgsPassthru[@]}")}"
exec "${JAVACMD}" \
-cp "${JVMClassPath}" \
-splash:"${ResourcesFolder}/${JVMSplashFile}" \
-Xdock:icon="${ResourcesFolder}/${CFBundleIconFile}" \
-Xdock:name="${CFBundleName}" \
${JVMOptions:+"$JVMOptions[@]}" }\
${JVMDefaultOptions:+$JVMDefaultOptions }\
"${JVMMainClass}"\
${MainArgsArr:+ "${MainArgsArr[@]}"}\
${ArgsPassthru:+ "${ArgsPassthru[@]}"}
================================================
FILE: package.sh
================================================
## Internal script for compiling and packaging DMG and EXE
## Compiles with appropriate properties file depending on if
## -b flag is set (if set, compile for BCM)
## Set the two globals at the top for your system
#!/bin/bash
set -e
shopt -s extglob
## GLOBALS: Set for your system
# executable for launch4j, which bundles EXE
LAUNCH4J_EXE="/Users/nchernia/Downloads/launch4j/launch4j"
# Lab At Large location, needed for certificates
LAL_DROPBOX="/Users/nchernia/Dropbox (Lab at Large)/"
printHelpAndExit() {
echo "Usage: ${0##*/} -bh"
echo " -v : 1.6.1 e.g. Required"
echo " -b: BCM-only version"
echo " -h: Print this help and exit"
exit "$1"
}
while getopts "v:bh" opt; do
case $opt in
v) VERSION=$OPTARG ;;
h) printHelpAndExit 0;;
b) BCM=1 ;;
[?]) printHelpAndExit 1;;
esac
done
if [ -z "$VERSION" ]
then
printHelpAndExit 1
fi
if [ -z "${BCM}" ]
then
CONFIG_FILE="config.xml"
else
CONFIG_FILE="config_bcm.xml"
fi
# these shouldn't need to be changed
BASE_DIR=$(pwd)
APP_NAME="Juicebox"
ARTIFACT_DIR="${BASE_DIR}/out/artifacts/Juicebox_jar"
DMG_BACKGROUND_IMG="Juicebox_bg.png"
# compile, bundle, sign
cd "${BASE_DIR}"
rm -r out
ant
#ant sign -- don't sign for DMG, problem with slowness
if [ -z "${BCM}" ]
then
ant bundle -Dversion="$VERSION"
else
ant bundlebcm # this is deprecated
fi
cd "${ARTIFACT_DIR}"
APP_EXE="${APP_NAME}.app/Contents/MacOS/JavaAppLauncher"
VOL_NAME="${APP_NAME}_${VERSION}"
DMG_TMP="${VOL_NAME}-temp.dmg"
DMG_FINAL="${VOL_NAME}.dmg"
STAGING_DIR="./Install"
# clear out any old data
rm -rf "${STAGING_DIR}" "${DMG_TMP}" "${DMG_FINAL}"
codesign -s "Erez Aiden" "${APP_NAME}".app --deep
# copy over the stuff we want in the final disk image to our staging dir
mkdir -p "${STAGING_DIR}"
cp -rpf "${APP_NAME}.app" "${STAGING_DIR}"
# ... cp anything else you want in the DMG - documentation, etc.
pushd "${STAGING_DIR}"
# strip the executable
#echo "Stripping ${APP_EXE}..."
#strip -u -r "${APP_EXE}"
# compress the executable if we have upx in PATH
# UPX: http://upx.sourceforge.net/
#if hash upx 2>/dev/null; then
# echo "Compressing (UPX) ${APP_EXE}..."
# upx -9 "${APP_EXE}"
#fi
# ... perform any other stripping/compressing of libs and executables
popd
# figure out how big our DMG needs to be
# assumes our contents are at least 1M!
SIZE=`du -sh "${STAGING_DIR}" | sed 's/\([0-9]*\)M\(.*\)/\1/'`
SIZE=`echo "${SIZE} + 1.0" | bc | awk '{print int($1+0.5)}'`
if [ $? -ne 0 ]; then
echo "Error: Cannot compute size of staging dir"
exit
fi
# create the temp DMG file
hdiutil create -srcfolder "${STAGING_DIR}" -volname "${VOL_NAME}" -fs HFS+ \
-fsargs "-c c=64,a=16,e=16" -format UDRW -size ${SIZE}M "${DMG_TMP}"
echo "Created DMG: ${DMG_TMP}"
# mount it and save the device
DEVICE=$(hdiutil attach -readwrite -noverify "${DMG_TMP}" | \
egrep '^/dev/' | sed 1q | awk '{print $1}')
sleep 2
# add a link to the Applications dir
echo "Add link to /Applications"
pushd /Volumes/"${VOL_NAME}"
ln -s /Applications
popd
# add a background image
mkdir /Volumes/"${VOL_NAME}"/.background
cp "${BASE_DIR}/${DMG_BACKGROUND_IMG}" /Volumes/"${VOL_NAME}"/.background/
# tell the Finder to resize the window, set the background,
# change the icon size, place the icons in the right position, etc.
echo '
tell application "Finder"
tell disk "'${VOL_NAME}'"
open
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
set the bounds of container window to {400, 100, 920, 460}
set viewOptions to the icon view options of container window
set arrangement of viewOptions to not arranged
set icon size of viewOptions to 72
set background picture of viewOptions to file ".background:'${DMG_BACKGROUND_IMG}'"
set position of item "'${APP_NAME}'.app" of container window to {140, 170}
set position of item "Applications" of container window to {380, 170}
close
open
update without registering applications
delay 2
end tell
end tell
' | osascript
sync
# unmount it
hdiutil detach "${DEVICE}"
# now make the final image a compressed disk image
echo "Creating compressed image"
hdiutil convert "${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "${DMG_FINAL}"
# clean up
rm -rf "${DMG_TMP}"
rm -rf "${STAGING_DIR}"
echo 'Done creating DMG'
###
### BUNDLE EXE
###
cd "${BASE_DIR}"
ant sign
cd "${BASE_DIR}"/l4j
# clean up any old versions
if ls *.exe 1> /dev/null 2>&1
then
rm *.exe
fi
# Package exe
"${LAUNCH4J_EXE}" "${CONFIG_FILE}"
# Sign. Uncomment below to change signature
#openssl pkcs12 -in ${LAL_DROPBOX}/important_jars/ErezSLieberman.p12 -nocerts\
# -nodes -out ${LAL_DROPBOX}/important_jars/certificate.pem
# Sign for the first time. Password juicebox. To create new password,
# uncomment above line, change "-pass" below
osslsigncode sign -certs "${LAL_DROPBOX}"/important_jars/erez_s_lieberman.pem \
-key "${LAL_DROPBOX}"/important_jars/certificate.pem -pass juicebox \
-n ${APP_NAME} -i http://aidenlab.org/ -in "${BASE_DIR}"/l4j/Juicebox.exe \
-out "${BASE_DIR}"/l4j/signed.exe
# Find the size of the signature in bytes
# i.e. sizeInBytesOf(signed.exe) - sizeInBytesOf(Juicebox.exe)
sizeInBytesSigned=$(ls -l "${BASE_DIR}"/l4j/signed.exe | awk '{print $5}')
sizeInBytesUnsigned=$(ls -l "${BASE_DIR}"/l4j/Juicebox.exe | awk '{print $5}')
# Print difference in Little Endian hex
echo "Edit ${BASE_DIR}/l4j/Juicebox.exe "
echo "with your favorite HEX editor to change last two bytes of the exe."
# This tells the zip file that there is a signature and what size.
echo "Edit with the following value, save, and press enter to continue:"
awk -v s1=$sizeInBytesSigned -v s2=$sizeInBytesUnsigned 'BEGIN{str=sprintf("%x", s1-s2); x=substr(str,3,2); x=x substr(str,1,2); print x}'
read line
# Sign the modified Juicebox.exe using above osslsigncode again
osslsigncode sign -certs "${LAL_DROPBOX}"/important_jars/erez_s_lieberman.pem \
-key "${LAL_DROPBOX}"/important_jars/certificate.pem -pass juicebox \
-n "Juicebox" -i http://aidenlab.org/ -in "${BASE_DIR}"/l4j/Juicebox.exe \
-out "${BASE_DIR}"/l4j/signed.exe
mv "${BASE_DIR}"/l4j/signed.exe "${ARTIFACT_DIR}"/Juicebox\ ${VERSION}.exe
mv "${ARTIFACT_DIR}"/Juicebox.jar "${ARTIFACT_DIR}"/Juicebox\ ${VERSION}.jar
echo "Done. The packaged executables live in ${ARTIFACT_DIR}"
================================================
FILE: src/images/manifest.mf
================================================
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build
================================================
FILE: src/juicebox/CommandBroadcaster.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2020 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package juicebox;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
//import java.io.InputStreamReader;
//import java.net.UnknownHostException;
//import java.util.ArrayList;
//import java.util.List;
/**
* @author jrobinso
* Date: 10/21/13
* Time: 2:59 PM
*/
class CommandBroadcaster {
public static int selfPort;
public static final int numPorts = 50;
public static void broadcast(String command) {
// Broadcast self port to other running instances
for (int p = 30000; p <= 30000 + numPorts - 1; p++) {
if (p == selfPort) continue; // don't broadcast to self
try {
CommandBroadcaster.broadcastCommand(command, p);
} catch (java.net.ConnectException e) {
// Expected
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
}
private static void broadcastCommand(String command, int port) throws IOException {
Socket socket = null;
PrintWriter out = null;
try {
socket = new Socket("127.0.0.1", port);
out = new PrintWriter(socket.getOutputStream(), true);
out.println(command);
} finally {
try {
if (out != null) out.close();
if (socket != null) socket.close();
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
}
}
================================================
FILE: src/juicebox/CommandExecutor.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2020 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package juicebox;
import org.broad.igv.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
class CommandExecutor {
private final HiC hic;
public CommandExecutor(HiC hic) {
this.hic = hic;
}
private List getArgs(String[] tokens) {
List args = new ArrayList<>(tokens.length);
for (String s : tokens) {
if (s.trim().length() > 0) {
args.add(s.trim());
}
}
return args;
}
public String execute(String command) {
List commandString = StringUtils.breakQuotedString(command, ' ');
List args = getArgs(commandString.toArray(new String[commandString.size()]));
String result = "OK";
System.err.println("Executing: " + command);
try {
if (args.size() > 0) {
String cmd = args.get(0).toLowerCase();
if (cmd.equals("setlocation")) {
if (args.size() > 7) {
String chrXName = args.get(1);
String chrYName = args.get(2);
String unitName = args.get(3);
HiC.Unit unit = HiC.valueOfUnit(unitName);
int binSize = Integer.parseInt(args.get(4));
double xOrigin = Double.parseDouble(args.get(5));
double yOrigin = Double.parseDouble(args.get(6));
double scaleFactor = Double.parseDouble(args.get(7));
hic.setLocation(chrXName, chrYName, unit, binSize, xOrigin, yOrigin, scaleFactor,
HiC.ZoomCallType.DIRECT, "Goto Sync", false);
} else {
result = "Not enough parameters";
}
}
} else {
result = "Unknown command string";
}
} catch (Exception e) {
System.err.println(e.getLocalizedMessage());
result = "Error: " + e.getMessage();
}
return result;
}
}
================================================
FILE: src/juicebox/CommandListener.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2021 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package juicebox;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
class CommandListener implements Runnable {
private static CommandListener listener;
private final Thread listenerThread;
private final HiC hic;
private int port = -1;
private ServerSocket serverSocket = null;
private Socket clientSocket = null;
private boolean halt = false;
private CommandListener(int port, HiC hic) throws IOException {
this.port = port;
this.hic = hic;
listenerThread = new Thread(this);
serverSocket = new ServerSocket(port);
}
public static synchronized void start(HiC hic) {
// Grab the first available port
int port = 0;
for (int p = 30000; p <= 30000 + CommandBroadcaster.numPorts - 1; p++) {
try {
listener = new CommandListener(p, hic);
listener.listenerThread.start();
port = p;
break;
} catch (IOException e) {
// Expected condition -- port in use
}
}
CommandBroadcaster.selfPort = port;
}
public static synchronized void halt() {
if (listener != null) {
listener.halt = true;
listener.listenerThread.interrupt();
listener.closeSockets();
listener = null;
}
}
/**
* Loop forever, processing client requests synchronously. The server is single threaded.
*/
public void run() {
CommandExecutor cmdExe = new CommandExecutor(hic);
System.out.println("Listening on port " + port);
try {
while (!halt) {
clientSocket = serverSocket.accept();
processClientRequest(cmdExe);
if (clientSocket != null) {
try {
clientSocket.close();
clientSocket = null;
} catch (IOException e) {
System.err.println("Error in client socket loop" + e.getLocalizedMessage());
}
}
}
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
/**
* Process a client request
*
* @param cmdExe
* @throws IOException
*/
private void processClientRequest(CommandExecutor cmdExe) throws IOException {
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()), HiCGlobals.bufferSize);
String cmd;
while (!halt && (cmd = in.readLine()) != null) {
if (cmd.equalsIgnoreCase("halt")) {
halt = true;
return;
}
cmdExe.execute(cmd);
}
} catch (IOException e) {
System.err.println("Error processing client session" + e.getLocalizedMessage());
} finally {
if (in != null) in.close();
}
}
private void closeSockets() {
if (clientSocket != null) {
try {
clientSocket.close();
clientSocket = null;
} catch (IOException e) {
System.err.println("Error closing clientSocket" + e.getLocalizedMessage());
}
}
if (serverSocket != null) {
try {
serverSocket.close();
serverSocket = null;
} catch (IOException e) {
System.err.println("Error closing server socket" + e.getLocalizedMessage());
}
}
}
}
================================================
FILE: src/juicebox/Context.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2020 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package juicebox;
import juicebox.data.basics.Chromosome;
import juicebox.windowui.HiCZoom;
/**
* @author jrobinso
* @since Aug 11, 2010
*/
public class Context {
private final Chromosome chromosome;
private HiCZoom zoom;
private double binOrigin = 0;
public Context(Chromosome chromosome) {
this.chromosome = chromosome;
}
public double getBinOrigin() {
return binOrigin;
}
public void setBinOrigin(double binOrigin) {
this.binOrigin = binOrigin;
}
public int getGenomicPositionOrigin() {
return (int) (binOrigin * zoom.getBinSize());
}
public HiCZoom getZoom() {
return zoom;
}
public void setZoom(HiCZoom zoom) {
this.zoom = zoom;
}
public long getChrLength() {
return chromosome.getLength();
}
public Chromosome getChromosome() {
return chromosome;
}
}
================================================
FILE: src/juicebox/DirectoryManager.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2020 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package juicebox;
import org.broad.igv.Globals;
import org.broad.igv.exceptions.DataLoadException;
import org.broad.igv.ui.util.FileDialogUtils;
import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
import java.io.File;
import java.io.IOException;
import java.util.prefs.Preferences;
/**
* @author Jim Robinson
* @since 3/19/12
*/
public class DirectoryManager {
private final static String HIC_DIR_USERPREF = "hicDir";
private static File USER_HOME;
private static File USER_DIRECTORY; // FileSystemView.getFileSystemView().getDefaultDirectory();
private static File HIC_DIRECTORY; // The HIC application directory
private static File getUserHome() {
if (USER_HOME == null) {
String userHomeString = System.getProperty("user.home");
USER_HOME = new File(userHomeString);
}
return USER_HOME;
}
/**
* The user directory. On Mac and Linux this should be the user home directory. On Windows platforms this
* is the "My Documents" directory.
*/
public static synchronized File getUserDirectory() {
if (USER_DIRECTORY == null) {
System.out.print("Fetching user directory... ");
USER_DIRECTORY = FileSystemView.getFileSystemView().getDefaultDirectory();
//Mostly for testing, in some environments USER_DIRECTORY can be null
if (USER_DIRECTORY == null) {
USER_DIRECTORY = getUserHome();
}
}
return USER_DIRECTORY;
}
public static File getHiCDirectory() {
if (HIC_DIRECTORY == null) {
// Hack for known Java / Windows bug. Attempt to remove (possible) read-only bit from user directory
if (System.getProperty("os.name").startsWith("Windows")) {
try {
Runtime.getRuntime().exec("attrib -r \"" + getUserDirectory().getAbsolutePath() + "\"");
} catch (Exception e) {
// We tried
}
}
HIC_DIRECTORY = getHiCDirectoryOverride();
// If still null, try the default place
if (HIC_DIRECTORY == null) {
File rootDir = getUserHome();
HIC_DIRECTORY = new File(rootDir, "juicebox");
if (!HIC_DIRECTORY.exists()) {
try {
boolean wasSuccessful = HIC_DIRECTORY.mkdir();
if (!wasSuccessful) {
System.err.println("Failed to create user directory!");
HIC_DIRECTORY = null;
}
} catch (Exception e) {
System.err.println("Error creating juicebox directory" + e.getLocalizedMessage());
}
}
}
// The HIC directory either doesn't exist or isn't writeable. This situation can arise with Windows Vista
// and Windows 7 due to a Java bug (http://bugs.sun.com/view_bug.do?bug_id=4787931)
if (HIC_DIRECTORY == null || !HIC_DIRECTORY.exists() || !canWrite(HIC_DIRECTORY)) {
if (Globals.isHeadless() || Globals.isSuppressMessages()) {
System.err.println("Cannot write to hic directory: " + HIC_DIRECTORY.getAbsolutePath());
HIC_DIRECTORY = (new File(".")).getParentFile();
} else {
int option = JOptionPane.showConfirmDialog(null,
"The default Hi-C directory (" + HIC_DIRECTORY + ") " +
"cannot be accessed. Click Yes to choose a new folder or No to exit. " +
"This folder will be used to create the 'hic' directory",
"Hi-C Directory Error", JOptionPane.YES_NO_OPTION);
if (option == JOptionPane.YES_OPTION) {
File parentDirectory = FileDialogUtils.chooseDirectory("Select a location for the hic directory", null);
if (parentDirectory != null) {
HIC_DIRECTORY = new File(parentDirectory, "hic");
HIC_DIRECTORY.mkdir();
Preferences prefs = Preferences.userNodeForPackage(Globals.class);
prefs.put(HIC_DIR_USERPREF, HIC_DIRECTORY.getAbsolutePath());
}
}
}
}
if (HIC_DIRECTORY == null || !HIC_DIRECTORY.canRead()) {
throw new DataLoadException("Cannot read from user directory", HIC_DIRECTORY.getAbsolutePath());
} else if (!canWrite(HIC_DIRECTORY)) {
throw new DataLoadException("Cannot write to user directory", HIC_DIRECTORY.getAbsolutePath());
}
System.err.println("HiC Directory: " + HIC_DIRECTORY.getAbsolutePath());
}
return HIC_DIRECTORY;
}
private static File getHiCDirectoryOverride() {
Preferences userPrefs = null;
File override = null;
try {
// See if an override location has been specified. This is stored with the Java Preferences API
userPrefs = Preferences.userNodeForPackage(Globals.class);
String userDir = userPrefs.get(HIC_DIR_USERPREF, null);
if (userDir != null) {
override = new File(userDir);
if (!override.exists()) {
override = null;
userPrefs.remove(HIC_DIR_USERPREF);
}
}
} catch (Exception e) {
assert userPrefs != null;
userPrefs.remove(HIC_DIR_USERPREF);
override = null;
System.err.println("Error creating user directory");
e.printStackTrace();
}
return override;
}
private static boolean canWrite(File directory) {
// There are bugs in the Windows Java JVM that can cause user directories to be non-writable (target fix is
// Java 7). The only way to know if the directory is writable for sure is to try to write something.
if (Globals.IS_WINDOWS) {
File testFile = null;
try {
testFile = new File(directory, "hic332415dsfjdsklt.testfile");
if (testFile.exists()) {
testFile.delete();
}
testFile.deleteOnExit();
testFile.createNewFile();
return testFile.exists();
} catch (IOException e) {
return false;
} finally {
assert testFile != null;
if (testFile.exists()) {
testFile.delete();
}
}
} else {
return directory.canWrite();
}
}
}
================================================
FILE: src/juicebox/HiC.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2021 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package juicebox;
import juicebox.data.*;
import juicebox.data.anchor.GenericLocus;
import juicebox.data.anchor.MotifAnchor;
import juicebox.data.basics.Chromosome;
import juicebox.gui.SuperAdapter;
import juicebox.tools.utils.common.MatrixTools;
import juicebox.track.*;
import juicebox.track.feature.Feature2D;
import juicebox.windowui.HiCZoom;
import juicebox.windowui.MatrixType;
import juicebox.windowui.NormalizationHandler;
import juicebox.windowui.NormalizationType;
import org.apache.commons.math.linear.RealMatrix;
import org.broad.igv.ui.util.MessageUtils;
import org.broad.igv.util.Pair;
import org.broad.igv.util.ResourceLocator;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
* This is the "model" class for the HiC viewer.
*/
public class HiC {
private final HiCTrackManager trackManager;
private final SuperAdapter superAdapter;
private final String eigString = "Eigenvector";
private final String ctrlEigString = "Ctrl_Eigenvector";
private final ZoomActionTracker zoomActionTracker = new ZoomActionTracker();
private final List highlightedFeatures = new ArrayList<>();
private double scaleFactor;
private String xPosition;
private String yPosition;
private MatrixType displayOption = MatrixType.OBSERVED;
private NormalizationType obsNormalizationType = NormalizationHandler.NONE;
private NormalizationType ctrlNormalizationType = NormalizationHandler.NONE;
private ChromosomeHandler chromosomeHandler;
private Dataset dataset;
private Dataset controlDataset;
private HiCZoom currentZoom;
//private MatrixZoomData matrixForReloadState;
private Context xContext;
private Context yContext;
private EigenvectorTrack eigenvectorTrack, controlEigenvectorTrack;
private ResourceTree resourceTree;
private LoadEncodeAction encodeAction;
private Point cursorPoint, diagonalCursorPoint, gwCursorPoint;
private Point selectedBin;
private boolean linkedMode;
private boolean m_zoomChanged;
private boolean m_displayOptionChanged;
private boolean m_normalizationTypeChanged;
private boolean showFeatureHighlight;
public HiC(SuperAdapter superAdapter) {
this.superAdapter = superAdapter;
trackManager = new HiCTrackManager(superAdapter, this);
//feature2DHandler = new Feature2DHandler();
m_zoomChanged = false;
m_displayOptionChanged = false;
m_normalizationTypeChanged = false;
}
/**
* @param string
* @return string with replacements of 000000 with M and 000 with K
*/
private static String cleanUpNumbersInName(String string) {
string = (new StringBuilder(string)).reverse().toString();
string = string.replaceAll("000000", "M").replaceAll("000", "K");
return (new StringBuilder(string)).reverse().toString();
}
public static HiC.Unit valueOfUnit(String unit) {
if (unit.equalsIgnoreCase(Unit.BP.toString())) {
return Unit.BP;
} else if (unit.equalsIgnoreCase(Unit.FRAG.toString())) {
return Unit.FRAG;
}
return null;
}
public Unit getDefaultUnit() {
return dataset.getBpZooms().size() > 0 ? Unit.BP : Unit.FRAG;
}
public void reset() {
dataset = null;
controlDataset = null;
displayOption = MatrixType.OBSERVED;
currentZoom = null;
resetContexts();
chromosomeHandler = null;
eigenvectorTrack = null;
controlEigenvectorTrack = null;
resourceTree = null;
encodeAction = null;
obsNormalizationType = NormalizationHandler.NONE;
ctrlNormalizationType = NormalizationHandler.NONE;
zoomActionTracker.clear();
clearFeatures();
}
//Todo why iterate through tracksToRemove if you end up calling clearFeatures() at the end?
public void clearTracksForReloadState() {
ArrayList tracksToRemove = new ArrayList<>(trackManager.getLoadedTracks());
for (HiCTrack trackToRemove : tracksToRemove) {
String name = trackToRemove.getName();
if (name.equalsIgnoreCase(eigString)) {
eigenvectorTrack = null;
} else if (name.equalsIgnoreCase(ctrlEigString)) {
controlEigenvectorTrack = null;
} else {
trackManager.removeTrack(trackToRemove);
}
}
clearFeatures();
superAdapter.updateTrackPanel();
}
private void clearFeatures() {
trackManager.clearTracks();
// feature2DHandler.clearLists();
}
/**
* Use setCursorPoint() to set cursorPoint to the last known mouse click position before calling this method
*/
public void undoZoomAction() {
zoomActionTracker.undoZoom();
ZoomAction currentZoomAction = zoomActionTracker.getCurrentZoomAction();
unsafeActuallySetZoomAndLocation(currentZoomAction.getChromosomeX(), currentZoomAction.getChromosomeY(),
currentZoomAction.getHiCZoom(), currentZoomAction.getGenomeX(), currentZoomAction.getGenomeY(),
currentZoomAction.getScaleFactor(), currentZoomAction.getResetZoom(), currentZoomAction.getZoomCallType(),
true, currentZoomAction.getResolutionLocked(), false);
}
/**
* Use setCursorPoint() to set cursorPoint to the last known mouse click position before calling this method
*/
public void redoZoomAction() {
zoomActionTracker.redoZoom();
ZoomAction currentZoomAction = zoomActionTracker.getCurrentZoomAction();
unsafeActuallySetZoomAndLocation(currentZoomAction.getChromosomeX(), currentZoomAction.getChromosomeY(),
currentZoomAction.getHiCZoom(), currentZoomAction.getGenomeX(), currentZoomAction.getGenomeY(),
currentZoomAction.getScaleFactor(), currentZoomAction.getResetZoom(), currentZoomAction.getZoomCallType(),
true, currentZoomAction.getResolutionLocked(), false);
}
public double getScaleFactor() {
return scaleFactor;
}
private void setScaleFactor(double scaleFactor) {
this.scaleFactor = Math.max(Math.min(50, scaleFactor), 1e-10);
}
public void loadEigenvectorTrack() {
if (eigenvectorTrack == null) {
eigenvectorTrack = new EigenvectorTrack(eigString, eigString, this, false);
}
if (controlEigenvectorTrack == null && isControlLoaded()) {
controlEigenvectorTrack = new EigenvectorTrack(ctrlEigString, ctrlEigString, this, true);
}
trackManager.add(eigenvectorTrack);
if (controlEigenvectorTrack != null && isControlLoaded()) {
trackManager.add(controlEigenvectorTrack);
}
}
public void refreshEigenvectorTrackIfExists() {
if (eigenvectorTrack != null) {
eigenvectorTrack.forceRefreshCache();
}
if (controlEigenvectorTrack != null) {
controlEigenvectorTrack.forceRefreshCache();
}
}
public ResourceTree getResourceTree() {
return resourceTree;
}
public void setResourceTree(ResourceTree rTree) {
resourceTree = rTree;
}
public void setEncodeAction(LoadEncodeAction eAction) {
encodeAction = eAction;
}
public boolean isLinkedMode() {
return linkedMode;
}
public void setLinkedMode(boolean linkedMode) {
this.linkedMode = linkedMode;
}
public List getLoadedTracks() {
return trackManager == null ? new ArrayList<>() : trackManager.getLoadedTracks();
}
public void unsafeLoadHostedTracks(List locators) {
trackManager.unsafeTrackLoad(locators);
}
public void unsafeLoadTrack(String path) {
trackManager.unsafeLoadTrackDirectPath(path);
}
public void loadCoverageTrack(String label) {
NormalizationType no = dataset.getNormalizationHandler().getNormTypeFromString(label);
trackManager.loadCoverageTrack(no, false);
if (isControlLoaded()) {
trackManager.loadCoverageTrack(no, true);
}
}
public void removeTrack(HiCTrack track) {
if (resourceTree != null) resourceTree.remove(track.getLocator());
if (encodeAction != null) encodeAction.remove(track.getLocator());
trackManager.removeTrack(track);
}
public void removeTrack(ResourceLocator locator) {
if (resourceTree != null) resourceTree.remove(locator);
if (encodeAction != null) encodeAction.remove(locator);
trackManager.removeTrack(locator);
}
public void moveTrack(HiCTrack track, boolean thisShouldBeMovedUp) {
if (thisShouldBeMovedUp) {
//move the track up
trackManager.moveTrackUp(track);
} else {
//move the track down
trackManager.moveTrackDown(track);
}
}
public Dataset getDataset() {
return dataset;
}
public void setDataset(Dataset dataset) {
this.dataset = dataset;
}
public Dataset getControlDataset() {
return controlDataset;
}
public void setControlDataset(Dataset controlDataset) {
this.controlDataset = controlDataset;
}
public void setSelectedChromosomes(Chromosome chrX, Chromosome chrY) {
this.xContext = new Context(chrX);
this.yContext = new Context(chrY);
refreshEigenvectorTrackIfExists();
}
public HiCZoom getZoom() {
return currentZoom;
}
public MatrixZoomData getZd() throws NullPointerException {
Matrix matrix = getMatrix();
if (matrix == null) {
throw new NullPointerException("Uninitialized matrix");
} else if (currentZoom == null) {
throw new NullPointerException("Uninitialized zoom");
} else {
return matrix.getZoomData(currentZoom);
}
}
public MatrixZoomData getControlZd() {
Matrix matrix = getControlMatrix();
if (matrix == null || currentZoom == null) {
return null;
} else {
return matrix.getZoomData(currentZoom);
}
}
public Matrix getControlMatrix() {
if (controlDataset == null || xContext == null || currentZoom == null) return null;
return controlDataset.getMatrix(xContext.getChromosome(), yContext.getChromosome());
}
public Context getXContext() {
return xContext;
}
public Context getYContext() {
return yContext;
}
public void resetContexts() {
this.xContext = null;
this.yContext = null;
}
public Point getCursorPoint() {
return cursorPoint;
}
public void setCursorPoint(Point point) {
this.cursorPoint = point;
}
public Point getDiagonalCursorPoint() {
return diagonalCursorPoint;
}
public void setDiagonalCursorPoint(Point point) {
this.diagonalCursorPoint = point;
}
public Point getGWCursorPoint() {
return gwCursorPoint;
}
public void setGWCursorPoint(Point point) {
gwCursorPoint = point;
}
public long[] getCurrentRegionWindowGenomicPositions() {
// address int overflow or exceeding bound issues
long xEndEdge = xContext.getGenomicPositionOrigin() +
(int) ((double) getZoom().getBinSize() * superAdapter.getHeatmapPanel().getWidth() / getScaleFactor());
if (xEndEdge < 0 || xEndEdge > xContext.getChromosome().getLength()) {
xEndEdge = xContext.getChromosome().getLength();
}
long yEndEdge = yContext.getGenomicPositionOrigin() +
(int) ((double) getZoom().getBinSize() * superAdapter.getHeatmapPanel().getHeight() / getScaleFactor());
if (yEndEdge < 0 || yEndEdge > yContext.getChromosome().getLength()) {
yEndEdge = yContext.getChromosome().getLength();
}
return new long[]{xContext.getGenomicPositionOrigin(), xEndEdge, yContext.getGenomicPositionOrigin(), yEndEdge};
}
public String getXPosition() {
return xPosition;
}
public void setXPosition(String txt) {
this.xPosition = txt;
}
public String getYPosition() {
return yPosition;
}
public void setYPosition(String txt) {
this.yPosition = txt;
}
public Matrix getMatrix() {
if (dataset == null) {
if (HiCGlobals.printVerboseComments) {
System.err.println("Dataset is null");
}
return null;
} else if (xContext == null) {
if (HiCGlobals.printVerboseComments) {
System.err.println("xContext is null");
}
return null;
} else if (yContext == null) {
if (HiCGlobals.printVerboseComments) {
System.err.println("yContext is null");
}
return null;
}
return dataset.getMatrix(xContext.getChromosome(), yContext.getChromosome());
}
public void setSelectedBin(Point point) {
if (point.equals(this.selectedBin)) {
this.selectedBin = null;
} else {
this.selectedBin = point;
}
}
public MatrixType getDisplayOption() {
return displayOption;
}
public void setDisplayOption(MatrixType newDisplay) {
if (this.displayOption != newDisplay) {
this.displayOption = newDisplay;
setDisplayOptionChanged();
}
}
public boolean isControlLoaded() {
return controlDataset != null;
}
public boolean isWholeGenome() {
return xContext != null && ChromosomeHandler.isAllByAll(xContext.getChromosome());
}
private void setZoomChanged() {
m_zoomChanged = true;
}
//Check zoom change value and reset.
public synchronized boolean testZoomChanged() {
if (m_zoomChanged) {
m_zoomChanged = false;
return true;
}
return false;
}
public void centerFragment(int fragmentX, int fragmentY) {
if (currentZoom != null) {
MatrixZoomData zd = getMatrix().getZoomData(currentZoom);
HiCGridAxis xAxis = zd.getXGridAxis();
HiCGridAxis yAxis = zd.getYGridAxis();
int binX;
int binY;
try {
binX = xAxis.getBinNumberForFragment(fragmentX);
//noinspection SuspiciousNameCombination
binY = yAxis.getBinNumberForFragment(fragmentY);
center(binX, binY);
} catch (RuntimeException error) {
superAdapter.launchGenericMessageDialog(error.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
public void centerBP(int bpX, int bpY) {
if (currentZoom != null && getMatrix() != null) {
MatrixZoomData zd = getMatrix().getZoomData(currentZoom);
HiCGridAxis xAxis = zd.getXGridAxis();
HiCGridAxis yAxis = zd.getYGridAxis();
int binX = xAxis.getBinNumberForGenomicPosition(bpX);
int binY = yAxis.getBinNumberForGenomicPosition(bpY);
center(binX, binY);
}
}
/**
* Center the bins in view at the current resolution.
*
* @param binX center X
* @param binY center Y
*/
public void center(double binX, double binY) {
double w = superAdapter.getHeatmapPanel().getWidth() / getScaleFactor(); // view width in bins
int newOriginX = (int) (binX - w / 2);
double h = superAdapter.getHeatmapPanel().getHeight() / getScaleFactor(); // view height in bins
int newOriginY = (int) (binY - h / 2);
moveTo(newOriginX, newOriginY);
}
/**
* Move by the specified delta (in bins)
*
* @param dxBins -- delta x in bins
* @param dyBins -- delta y in bins
*/
public void moveBy(double dxBins, double dyBins) {
final double newX = xContext.getBinOrigin() + dxBins;
final double newY = yContext.getBinOrigin() + dyBins;
moveTo(newX, newY);
}
/**
* Move to the specified origin (in bins)
*
* @param newBinX new location X
* @param newBinY new location Y
*/
private void moveTo(double newBinX, double newBinY) {
try {
MatrixZoomData zd = getZd();
final double wBins = (superAdapter.getHeatmapPanel().getWidth() / getScaleFactor());
double maxX = zd.getXGridAxis().getBinCount() - wBins;
final double hBins = (superAdapter.getHeatmapPanel().getHeight() / getScaleFactor());
double maxY = zd.getYGridAxis().getBinCount() - hBins;
double x = Math.max(0, Math.min(maxX, newBinX));
double y = Math.max(0, Math.min(maxY, newBinY));
xContext.setBinOrigin(x);
yContext.setBinOrigin(y);
superAdapter.repaint();
} catch (Exception e) {
e.printStackTrace();
}
if (linkedMode) {
broadcastLocation();
}
}
private void setDisplayOptionChanged() {
m_displayOptionChanged = true;
}
//Check zoom change value and reset.
public synchronized boolean testDisplayOptionChanged() {
if (m_displayOptionChanged) {
m_displayOptionChanged = false;
return true;
}
return false;
}
private void setNormalizationTypeChanged() {
m_normalizationTypeChanged = true;
}
//Check zoom change value and reset.
public synchronized boolean testNormalizationTypeChanged() {
if (m_normalizationTypeChanged) {
m_normalizationTypeChanged = false;
return true;
}
return false;
}
public NormalizationType getObsNormalizationType() {
return obsNormalizationType;
}
public void setObsNormalizationType(String label) {
NormalizationType option = dataset.getNormalizationHandler().getNormTypeFromString(label);
if (this.obsNormalizationType != option) {
this.obsNormalizationType = option;
setNormalizationTypeChanged();
}
}
public NormalizationType getControlNormalizationType() {
return ctrlNormalizationType;
}
public void setControlNormalizationType(String label) {
NormalizationType option = dataset.getNormalizationHandler().getNormTypeFromString(label);
if (this.ctrlNormalizationType != option) {
this.ctrlNormalizationType = option;
setNormalizationTypeChanged();
}
}
public double[] getEigenvector(final int chrIdx, final int n, boolean isControl) {
if (isControl) {
if (controlDataset == null) return null;
Chromosome chr = chromosomeHandler.getChromosomeFromIndex(chrIdx);
return controlDataset.getEigenvector(chr, currentZoom, n, ctrlNormalizationType);
} else {
if (dataset == null) return null;
Chromosome chr = chromosomeHandler.getChromosomeFromIndex(chrIdx);
return dataset.getEigenvector(chr, currentZoom, n, obsNormalizationType);
}
}
public ExpectedValueFunction getExpectedValues() {
if (dataset == null) return null;
return dataset.getExpectedValues(currentZoom, obsNormalizationType);
}
public ExpectedValueFunction getExpectedControlValues() {
if (controlDataset == null) return null;
return controlDataset.getExpectedValues(currentZoom, ctrlNormalizationType);
}
public NormalizationVector getNormalizationVector(int chrIdx) {
if (dataset == null) return null;
return dataset.getNormalizationVector(chrIdx, currentZoom, obsNormalizationType);
}
public NormalizationVector getControlNormalizationVector(int chrIdx) {
if (controlDataset == null) return null;
return controlDataset.getNormalizationVector(chrIdx, currentZoom, ctrlNormalizationType);
}
// Note - this is an inefficient method, used to support tooltip text only.
public float getNormalizedObservedValue(int binX, int binY) {
float val = Float.NaN;
try {
val = getZd().getObservedValue(binX, binY, obsNormalizationType);
} catch (Exception e) {
e.printStackTrace();
}
return val;
}
public float getNormalizedControlValue(int binX, int binY) {
float val = Float.NaN;
try {
val = getControlZd().getObservedValue(binX, binY, ctrlNormalizationType);
} catch (Exception e) {
e.printStackTrace();
}
return val;
}
/**
* Called from alt-drag zoom
*
* @param xBP0
* @param yBP0
* @param targetBinSize
*/
public void zoomToDrawnBox(final long xBP0, final long yBP0, final double targetBinSize) {
HiCZoom newZoom = currentZoom;
if (!isResolutionLocked()) {
List zoomList = currentZoom.getUnit() == HiC.Unit.BP ? dataset.getBpZooms() : dataset.getFragZooms();
for (int i = zoomList.size() - 1; i >= 0; i--) {
if (zoomList.get(i).getBinSize() >= targetBinSize) {
newZoom = zoomList.get(i);
break;
}
}
// this addresses draw box to zoom when down from low res pearsons
// it can't zoom all the way in, but can zoom in a little more up to 500K
if (isInPearsonsMode() && newZoom.getBinSize() < HiCGlobals.MAX_PEARSON_ZOOM) {
for (int i = zoomList.size() - 1; i >= 0; i--) {
if (zoomList.get(i).getBinSize() >= HiCGlobals.MAX_PEARSON_ZOOM) {
newZoom = zoomList.get(i);
break;
}
}
}
}
safeActuallySetZoomAndLocation(newZoom, xBP0, yBP0, newZoom.getBinSize() / targetBinSize, false,
ZoomCallType.DRAG, "DragZoom", true);
}
/**
* Triggered by syncs, goto, and load state.
*/
//reloading the previous location
public void setLocation(String chrXName, String chrYName, HiC.Unit unit, int binSize, double xOrigin,
double yOrigin, double scaleFactor, ZoomCallType zoomCallType, String message,
boolean allowLocationBroadcast) {
HiCZoom newZoom = currentZoom;
if (currentZoom.getBinSize() != binSize) {
newZoom = new HiCZoom(unit, binSize);
}
safeActuallySetZoomAndLocation(chrXName, chrYName, newZoom, (int) xOrigin, (int) yOrigin, scaleFactor,
true, zoomCallType, message, allowLocationBroadcast);
}
public void unsafeSetLocation(String chrXName, String chrYName, String unitName, int binSize, double xOrigin,
double yOrigin, double scaleFactor, ZoomCallType zoomCallType, boolean allowLocationBroadcast) {
HiCZoom newZoom = currentZoom;
if (currentZoom.getBinSize() != binSize) {
newZoom = new HiCZoom(HiC.valueOfUnit(unitName), binSize);
}
unsafeActuallySetZoomAndLocation(chrXName, chrYName, newZoom, (int) xOrigin, (int) yOrigin, scaleFactor,
true, zoomCallType, allowLocationBroadcast, isResolutionLocked() ? 1 : 0, true);
}
private boolean safeActuallySetZoomAndLocation(HiCZoom newZoom, long genomeX, long genomeY, double scaleFactor,
boolean resetZoom, ZoomCallType zoomCallType, String message,
boolean allowLocationBroadcast) {
return safeActuallySetZoomAndLocation(xContext.getChromosome().toString(), yContext.getChromosome().toString(),
newZoom, genomeX, genomeY, scaleFactor, resetZoom, zoomCallType, message, allowLocationBroadcast);
}
private boolean safeActuallySetZoomAndLocation(final String chrXName, final String chrYName,
final HiCZoom newZoom, final long genomeX, final long genomeY,
final double scaleFactor, final boolean resetZoom,
final ZoomCallType zoomCallType, String message,
final boolean allowLocationBroadcast) {
final boolean[] returnVal = new boolean[1];
superAdapter.executeLongRunningTask(new Runnable() {
@Override
public void run() {
returnVal[0] = unsafeActuallySetZoomAndLocation(chrXName, chrYName, newZoom, genomeX, genomeY, scaleFactor,
resetZoom, zoomCallType, allowLocationBroadcast, isResolutionLocked() ? 1 : 0, true);
}
}, message);
return returnVal[0];
}
/**
* *************************************************************
* Official Method for setting the zoom and location for heatmap
* DO NOT IMPLEMENT A NEW FUNCTION
* Make the necessary customizations, then call this function
* *************************************************************
*
* @param newZoom
* @param genomeX
* @param genomeY
* @param scaleFactor (pass -1 if scaleFactor should be calculated)
* @param resolutionLocked (pass -1 if status of lock button should not be saved)
* @param storeZoomAction (pass false if function is being used to undo/redo zoom, true otherwise)
* @return
*/
public boolean unsafeActuallySetZoomAndLocation(String chrXName, String chrYName,
HiCZoom newZoom, long genomeX, long genomeY, double scaleFactor,
boolean resetZoom, ZoomCallType zoomCallType,
boolean allowLocationBroadcast, int resolutionLocked, boolean storeZoomAction) {
if (dataset == null) return false; // No data in view
boolean chromosomesChanged = !(xContext.getChromosome().equals(chromosomeHandler.getChromosomeFromName(chrXName)) &&
yContext.getChromosome().equals(chromosomeHandler.getChromosomeFromName(chrYName)));
if (chrXName.length() > 0 && chrYName.length() > 0) {
setChromosomesFromBroadcast(chrXName, chrYName);
//We might end with All->All view, make sure normalization state is updated accordingly...
superAdapter.getMainViewPanel().setNormalizationDisplayState(superAdapter.getHiC());
}
if (newZoom == null) {
System.err.println("Invalid zoom " + newZoom);
}
Chromosome chrX = chromosomeHandler.getChromosomeFromName(chrXName);
Chromosome chrY = chromosomeHandler.getChromosomeFromName(chrYName);
final Matrix matrix = dataset.getMatrix(chrX, chrY);
if (matrix == null) {
superAdapter.launchGenericMessageDialog("Sorry, this region is not available", "Matrix unavailable",
JOptionPane.WARNING_MESSAGE);
return false;
}
MatrixZoomData newZD = matrix.getZoomData(newZoom);
if (ChromosomeHandler.isAllByAll(chrX)) {
newZD = matrix.getFirstZoomData(Unit.BP);
}
if (newZD == null) {
superAdapter.launchGenericMessageDialog("Sorry, this zoom is not available", "Zoom unavailable",
JOptionPane.WARNING_MESSAGE);
return false;
}
Matrix preZoomMatrix = getMatrix();
double preZoomScaleFactor = getScaleFactor();
Context preZoomXContext = xContext;
Context preZoomYContext = yContext;
HiCZoom preZoomHiCZoom = currentZoom;
if (resolutionLocked >= 0) {
adjustLockButton(resolutionLocked != 0);
}
currentZoom = newZoom;
xContext.setZoom(currentZoom);
yContext.setZoom(currentZoom);
if (scaleFactor > 0) {
setScaleFactor(scaleFactor);
} else {
long maxBinCount = Math.max(newZD.getXGridAxis().getBinCount(), newZD.getYGridAxis().getBinCount());
double defaultScaleFactor = Math.max(1.0, (double) superAdapter.getHeatmapPanel().getMinimumDimension() / maxBinCount);
setScaleFactor(defaultScaleFactor);
}
int binX = newZD.getXGridAxis().getBinNumberForGenomicPosition(genomeX);
int binY = newZD.getYGridAxis().getBinNumberForGenomicPosition(genomeY);
if (zoomCallType == ZoomCallType.INITIAL || zoomCallType == ZoomCallType.STANDARD) {
if (storeZoomAction || chromosomesChanged) {
center(binX, binY);
} else if (preZoomHiCZoom != null && getCursorPoint() != null) {
Point standardUnzoomCoordinates = computeStandardUnzoomCoordinates(preZoomMatrix, preZoomXContext,
preZoomYContext, newZD, preZoomHiCZoom, preZoomScaleFactor);
center(standardUnzoomCoordinates.getX(), standardUnzoomCoordinates.getY());
}
} else if (zoomCallType == ZoomCallType.DRAG) {
xContext.setBinOrigin(binX);
yContext.setBinOrigin(binY);
} else if (zoomCallType == ZoomCallType.DIRECT) {
xContext.setBinOrigin(genomeX);
yContext.setBinOrigin(genomeY);
}
// Notify HeatmapPanel render that zoom has changed. Render should update zoom slider once with previous range values
setZoomChanged();
if (resetZoom) {
superAdapter.updateAndResetZoom(newZoom);
} else {
superAdapter.updateZoom(newZoom);
}
superAdapter.refresh();
if (linkedMode && allowLocationBroadcast) {
broadcastLocation();
}
if (storeZoomAction) {
ZoomAction newZoomAction = new ZoomAction(chrXName, chrYName, newZoom, genomeX, genomeY, scaleFactor,
resetZoom, zoomCallType, resolutionLocked);
if (zoomActionTracker.getCurrentZoomAction() == null) {
this.zoomActionTracker.addZoomState(newZoomAction);
} else if (!zoomActionTracker.getCurrentZoomAction().equals(newZoomAction)) {
this.zoomActionTracker.addZoomState(newZoomAction);
}
}
return true;
}
private Point computeStandardUnzoomCoordinates(Matrix preZoomMatrix, Context preZoomXContext, Context preZoomYContext,
MatrixZoomData newZD, HiCZoom preZoomHiCZoom, double preZoomScaleFactor) {
double xMousePos = cursorPoint.getX();
double yMousePos = cursorPoint.getY();
double preZoomCenterBinX = preZoomXContext.getBinOrigin() + xMousePos / preZoomScaleFactor;
double preZoomCenterBinY = preZoomYContext.getBinOrigin() + yMousePos / preZoomScaleFactor;
long preZoomBinCountX = preZoomMatrix.getZoomData(preZoomHiCZoom).getXGridAxis().getBinCount();
long preZoomBinCountY = preZoomMatrix.getZoomData(preZoomHiCZoom).getYGridAxis().getBinCount();
long postZoomBinCountX = newZD.getXGridAxis().getBinCount();
long postZoomBinCountY = newZD.getYGridAxis().getBinCount();
return new Point((int) (preZoomCenterBinX / preZoomBinCountX * postZoomBinCountX),
(int) (preZoomCenterBinY / preZoomBinCountY * postZoomBinCountY));
}
private void adjustLockButton(boolean savedResolutionLocked) {
if (isResolutionLocked() != savedResolutionLocked) {
superAdapter.getMainViewPanel().getResolutionSlider().setResolutionLocked(savedResolutionLocked);
}
}
private void setChromosomesFromBroadcast(String chrXName, String chrYName) {
if (!chrXName.equals(xContext.getChromosome().getName()) || !chrYName.equals(yContext.getChromosome().getName())) {
Chromosome chrX = chromosomeHandler.getChromosomeFromName(chrXName);
Chromosome chrY = chromosomeHandler.getChromosomeFromName(chrYName);
if (chrX == null || chrY == null) {
//System.out.println("Most probably origin is a different species saved location or sync/link between two different species maps.");
return;
}
this.xContext = new Context(chrX);
this.yContext = new Context(chrY);
superAdapter.setSelectedChromosomesNoRefresh(chrX, chrY);
refreshEigenvectorTrackIfExists();
}
}
public void broadcastLocation() {
String command = getLocationDescription();
CommandBroadcaster.broadcast(command);
}
public String getLocationDescription() {
String xChr = xContext.getChromosome().getName();
String yChr = yContext.getChromosome().getName();
// if (!xChr.toLowerCase().equals("assembly") && !(xChr.toLowerCase().contains("chr"))) xChr = "chr" + xChr;
// if (!yChr.toLowerCase().equals("assembly") && !(yChr.toLowerCase().contains("chr"))) yChr = "chr" + yChr;
return "setlocation " + xChr + " " + yChr + " " + currentZoom.getUnit().toString() + " " + currentZoom.getBinSize() + " " +
xContext.getBinOrigin() + " " + yContext.getBinOrigin() + " " + getScaleFactor();
}
public String getDefaultLocationDescription() {
String xChr = xContext.getChromosome().getName();
String yChr = yContext.getChromosome().getName();
// if (!xChr.toLowerCase().equals("assembly") && !(xChr.toLowerCase().contains("chr"))) xChr = "chr" + xChr;
// if (!yChr.toLowerCase().equals("assembly") && !(yChr.toLowerCase().contains("chr"))) yChr = "chr" + yChr;
return xChr + "@" + (long) (xContext.getBinOrigin() * currentZoom.getBinSize()) + "_" +
yChr + "@" + (long) (yContext.getBinOrigin() * currentZoom.getBinSize());
}
public void restoreLocation(String cmd) {
CommandExecutor cmdExe = new CommandExecutor(this);
cmdExe.execute(cmd);
if (linkedMode) {
broadcastLocation();
}
}
public void loadLoopList(String path) {
superAdapter.getActiveLayerHandler().loadLoopList(path, chromosomeHandler);
}
public void generateTrackFromLocation(int mousePos, boolean isHorizontal) {
if (!MatrixType.isObservedOrControl(displayOption)) {
SuperAdapter.showMessageDialog("This feature is only available for Observed or Control views");
return;
}
// extract the starting position
long binStartPosition = (long) (getXContext().getBinOrigin() + mousePos / getScaleFactor());
if (isHorizontal) binStartPosition = (long) (getYContext().getBinOrigin() + mousePos / getScaleFactor());
// Initialize default file name
String filename = displayOption == MatrixType.OBSERVED ? "obs" : "ctrl";
filename += isHorizontal ? "_horz" : "_vert";
filename += "_bin" + binStartPosition + "_res" + currentZoom.getBinSize();
filename = cleanUpNumbersInName(filename);
// allow user to customize or change the name
filename = MessageUtils.showInputDialog("Enter a name for the resulting .wig file", filename);
if (filename == null || filename.equalsIgnoreCase("null"))
return;
File outputWigFile = new File(DirectoryManager.getHiCDirectory(), filename + ".wig");
SuperAdapter.showMessageDialog("Data will be saved to " + outputWigFile.getAbsolutePath());
Chromosome chromosomeForPosition = getXContext().getChromosome();
if (isHorizontal) chromosomeForPosition = getYContext().getChromosome();
safeSave1DTrackToWigFile(chromosomeForPosition, outputWigFile, binStartPosition);
}
/*
public List getAllVisibleLoops() {
return feature2DHandler.getAllVisibleLoops();
}
/*
public List getVisibleFeatures(int chrIdx1, int chrIdx2) {
return feature2DHandler.getVisibleFeatures(chrIdx1, chrIdx2);
}
public List findNearbyFeatures(MatrixZoomData zd, int chrIdx1, int chrIdx2, int x, int y, int n) {
double binOriginX = getXContext().getBinOrigin();
double binOriginY = getYContext().getBinOrigin();
double scale = getScaleFactor();
return feature2DHandler.getNearbyFeatures(zd, chrIdx1, chrIdx2, x, y, n, binOriginX, binOriginY, scale);
}
public List> findNearbyFeaturePairs(MatrixZoomData zd, int chrIdx1, int chrIdx2, int x,
int y, int n) {
double binOriginX = getXContext().getBinOrigin();
double binOriginY = getYContext().getBinOrigin();
double scale = getScaleFactor();
return feature2DHandler.getNearbyFeaturePairs(zd, chrIdx1, chrIdx2, x, y, n, binOriginX, binOriginY, scale);
}
*/
/*
public void removeLoadedAnnotation(String path) {
feature2DHandler.removeFeaturePath(path);
}
*/
private void safeSave1DTrackToWigFile(final Chromosome chromosomeForPosition, final File outputWigFile,
final long binStartPosition) {
superAdapter.getMainWindow().executeLongRunningTask(new Runnable() {
@Override
public void run() {
try {
PrintWriter printWriter = new PrintWriter(outputWigFile);
unsafeSave1DTrackToWigFile(chromosomeForPosition, printWriter, binStartPosition);
printWriter.close();
if (outputWigFile.exists() && outputWigFile.length() > 0) {
// TODO this still doesn't add to the resource tree / load annotation dialog box
//superAdapter.getTrackLoadAction();
//getResourceTree().add1DCustomTrack(outputWigFile);
HiC.this.unsafeLoadTrack(outputWigFile.getAbsolutePath());
LoadAction loadAction = superAdapter.getTrackLoadAction();
loadAction.checkBoxesForReload(outputWigFile.getName());
}
} catch (Exception e) {
System.err.println("Unable to generate and save 1D HiC track");
}
}
}, "Saving_1D_track_as_wig");
}
private void unsafeSave1DTrackToWigFile(Chromosome chromosomeForPosition, PrintWriter printWriter,
long binStartPosition) {
// todo could crash with custom chromosomes - so make sure this doesn't get called on those chromosomes
int resolution = getZoom().getBinSize();
for (Chromosome chromosome : chromosomeHandler.getChromosomeArrayWithoutAllByAll()) {
Matrix matrix = null;
if (displayOption == MatrixType.OBSERVED) {
matrix = dataset.getMatrix(chromosomeForPosition, chromosome);
} else if (displayOption == MatrixType.CONTROL) {
matrix = controlDataset.getMatrix(chromosomeForPosition, chromosome);
}
if (matrix == null) continue;
MatrixZoomData zd = matrix.getZoomData(currentZoom);
printWriter.println("fixedStep chrom=chr" + chromosome.getName().replace("chr", "")
+ " start=1 step=" + resolution + " span=" + resolution);
long[] regionIndices;
if (chromosomeForPosition.getIndex() < chromosome.getIndex()) {
regionIndices = new long[]{binStartPosition, binStartPosition, 0, chromosome.getLength()};
} else {
regionIndices = new long[]{0, chromosome.getLength(), binStartPosition, binStartPosition};
}
zd.dump1DTrackFromCrossHairAsWig(printWriter, binStartPosition,
chromosomeForPosition.getIndex() == chromosome.getIndex(), regionIndices,
obsNormalizationType, displayOption);
}
}
public void generateRainbowBed() {
// Initialize default file name
String filename = "temp.rainbow";
File outputBedFile = new File(DirectoryManager.getHiCDirectory(), filename + ".bed");
// SuperAdapter.showMessageDialog("Data will be saved to " + outputWigFile.getAbsolutePath());
Chromosome chromosome = getXContext().getChromosome();
safeGenerateRainbowBed(chromosome, outputBedFile);
}
private void safeGenerateRainbowBed(final Chromosome chromosome, final File outputBedFile) {
superAdapter.getMainWindow().executeLongRunningTask(new Runnable() {
@Override
public void run() {
try {
PrintWriter printWriter = new PrintWriter(outputBedFile);
unsafeGenerateRainbowBed(chromosome, printWriter);
printWriter.close();
if (outputBedFile.exists() && outputBedFile.length() > 0) {
// TODO this still doesn't add to the resource tree / load annotation dialog box
//superAdapter.getTrackLoadAction();
//getResourceTree().add1DCustomTrack(outputWigFile);
HiC.this.unsafeLoadTrack(outputBedFile.getAbsolutePath());
LoadAction loadAction = superAdapter.getTrackLoadAction();
loadAction.checkBoxesForReload(outputBedFile.getName());
}
} catch (Exception e) {
System.err.println("Unable to generate rainbow track");
}
}
}, "Saving rainbow bed file.");
}
private void unsafeGenerateRainbowBed(Chromosome chromosome, PrintWriter printWriter) {
printWriter.println("track name=\"Rainbow track\" description=\"Rainbow track\" visibility=2 itemRgb=\"On\"");
int resolution = getZoom().getBinSize();
long size = chromosome.getLength() / resolution + 1;
for (int i = 0; i < size; i++) {
printWriter.println(chromosome.getName() + "\t" + i * resolution + "\t" + ((i + 1) * resolution) + "\t-\t0\t+\t" + i * resolution + "\t" + ((i + 1) * resolution) + "\t" + getRgb(i, size));
}
}
private String getRgb(int i, long size) {
int red = (int) Math.floor(127 * Math.sin(Math.PI / size * 2 * i + 0 * Math.PI * 2 / 3)) + 128;
int blue = (int) Math.floor(127 * Math.sin(Math.PI / size * 2 * i + 1 * Math.PI * 2 / 3)) + 128;
int green = (int) Math.floor(127 * Math.sin(Math.PI / size * 2 * i + 2 * Math.PI * 2 / 3)) + 128;
return red + "," + green + "," + blue;
}
public boolean isInPearsonsMode() {
return MatrixType.isPearsonType(displayOption);
}
public boolean isPearsonEdgeCaseEncountered(HiCZoom zoom) {
return isInPearsonsMode() && zoom.getBinSize() < HiCGlobals.MAX_PEARSON_ZOOM;
}
public boolean isResolutionLocked() {
return superAdapter.isResolutionLocked() ||
// pearson can't zoom in
// even though it should never be less, I think we should try to catch it
// (i.e. <= rather than ==)?
(isInPearsonsMode() && currentZoom.getBinSize() <= HiCGlobals.MAX_PEARSON_ZOOM);
}
public boolean isPearsonsNotAvailableForFile(boolean isControl) {
try {
if (isControl) {
MatrixZoomData cZd = getControlZd();
return cZd.getPearsons(controlDataset.getExpectedValues(cZd.getZoom(), ctrlNormalizationType)) == null;
} else {
MatrixZoomData zd = getZd();
return zd.getPearsons(dataset.getExpectedValues(zd.getZoom(), obsNormalizationType)) == null;
}
} catch (Exception e) {
return true;
}
}
public boolean isPearsonsNotAvailableAtSpecificZoom(boolean isControl, HiCZoom zoom) {
try {
if (isControl) {
return getControlZd().getPearsons(controlDataset.getExpectedValues(zoom, ctrlNormalizationType)) == null;
} else {
return getZd().getPearsons(dataset.getExpectedValues(zoom, obsNormalizationType)) == null;
}
} catch (Exception e) {
return true;
}
}
public Color getColorForRuler() {
if (MatrixType.isPearsonType(displayOption)) {
return Color.WHITE;
} else {
if (HiCGlobals.isDarkulaModeEnabled) {
return HiCGlobals.DARKULA_RULER_LINE_COLOR;
} else {
return HiCGlobals.RULER_LINE_COLOR;
}
}
}
public boolean isVSTypeDisplay() {
return MatrixType.isVSTypeDisplay(displayOption);
}
public boolean isInControlPearsonsMode() {
return MatrixType.isControlPearsonType(displayOption);
}
public String getColorScaleKey() {
try {
return getZd().getColorScaleKey(displayOption, obsNormalizationType, ctrlNormalizationType);
} catch (Exception ignored) {
}
return null;
}
public List getHighlightedFeatures() {
if (showFeatureHighlight) {
return highlightedFeatures;
}
return new ArrayList<>();
}
public void setHighlightedFeatures(List highlightedFeatures) {
this.highlightedFeatures.clear();
this.highlightedFeatures.addAll(highlightedFeatures);
}
public void setShowFeatureHighlight(boolean showFeatureHighlight) {
this.showFeatureHighlight = showFeatureHighlight;
}
public ChromosomeHandler getChromosomeHandler() {
return chromosomeHandler;
}
public void setChromosomeHandler(ChromosomeHandler chromosomeHandler) {
this.chromosomeHandler = chromosomeHandler;
dataset.setChromosomeHandler(chromosomeHandler);
if (controlDataset != null) controlDataset.setChromosomeHandler(chromosomeHandler);
}
public ZoomActionTracker getZoomActionTracker() {
return this.zoomActionTracker;
}
public void clearAllMatrixZoomDataCache() {
clearAllCacheForDataset(dataset);
if (isControlLoaded()) {
clearAllCacheForDataset(controlDataset);
}
}
private void clearAllCacheForDataset(Dataset ds) {
ds.clearCache(false);
}
public List> getRTreeHandlerIntersectingFeatures(String name, int g1, int g2) {
try {
return ((CustomMatrixZoomData) getZd()).getRTreeHandlerIntersectingFeatures(name, g1, g2);
} catch (Exception ignored) {
return new ArrayList<>();
}
}
public String[] getNormalizationOptions(boolean isControl) {
if (isControl) {
if (controlDataset == null || controlDataset.getVersion() < HiCGlobals.minVersion) {
return new String[]{NormalizationHandler.NONE.getDescription()};
} else {
ArrayList tmp = new ArrayList<>();
tmp.add(NormalizationHandler.NONE.getDescription());
for (NormalizationType t : controlDataset.getNormalizationTypes()) {
tmp.add(t.getDescription());
}
return tmp.toArray(new String[tmp.size()]);
}
} else {
if (dataset.getVersion() < HiCGlobals.minVersion) {
return new String[]{NormalizationHandler.NONE.getDescription()};
} else {
ArrayList tmp = new ArrayList<>();
tmp.add(NormalizationHandler.NONE.getDescription());
for (NormalizationType t : dataset.getNormalizationTypes()) {
tmp.add(t.getDescription());
}
return tmp.toArray(new String[tmp.size()]);
}
}
}
public void exportDataCenteredAboutRegion(int xBinPos, int yBinPos) throws IOException {
int radius = 20;
// Initialize default file name
String filename = displayOption == MatrixType.OBSERVED ? "obs" : "ctrl";
filename += "_bin_" + xBinPos + "_" + yBinPos + "_res_" + currentZoom.getBinSize();
filename = cleanUpNumbersInName(filename);
// allow user to customize or change the name
filename = MessageUtils.showInputDialog("Enter a name for the resulting .txt file", filename);
if (filename == null || filename.equalsIgnoreCase("null"))
return;
String radiusSize = MessageUtils.showInputDialog("What radius of pixels around the selected point would you like", "" + radius);
try {
radius = Integer.parseInt(radiusSize);
} catch (Exception ignored) {
radius = 20;
}
File outputMatrixFile = new File(DirectoryManager.getHiCDirectory(), filename + ".txt");
SuperAdapter.showMessageDialog("Data will be saved to " + outputMatrixFile.getAbsolutePath());
// extract the starting position
int xbinStartPosition = (int) (getXContext().getBinOrigin() + xBinPos / getScaleFactor());
int ybinStartPosition = (int) (getYContext().getBinOrigin() + yBinPos / getScaleFactor());
int binXStart = xbinStartPosition - radius;
int binXEnd = xbinStartPosition + radius;
int binYStart = ybinStartPosition - radius;
int binYEnd = ybinStartPosition + radius;
int matrixWidth = 2 * radius + 1;
MatrixZoomData requestedZd = getZd();
NormalizationType requestedNormType = getObsNormalizationType();
if (MatrixType.isSimpleControlType(displayOption)) {
requestedZd = getControlZd();
requestedNormType = getControlNormalizationType();
}
RealMatrix realMatrix = HiCFileTools.extractLocalBoundedRegion(requestedZd, binXStart, binXEnd, binYStart, binYEnd,
matrixWidth, matrixWidth, requestedNormType, true);
MatrixTools.saveMatrixTextV2(outputMatrixFile.getAbsolutePath(), realMatrix);
}
public void createNewDynamicResolutions(int newResolution) {
dataset.addDynamicResolution(newResolution);
if (controlDataset != null) {
controlDataset.addDynamicResolution(newResolution);
}
}
// use REVERSE for only undoing and redoing zoom actions
public enum ZoomCallType {
STANDARD, DRAG, DIRECT, INITIAL, REVERSE
}
public enum Unit {BP, FRAG}
}
================================================
FILE: src/juicebox/HiCGlobals.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2024 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package juicebox;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Muhammad Shamim
* @since 11/25/14
*/
public class HiCGlobals {
public static final String versionNum = "2.24.00";
public static final String juiceboxTitle = "[Juicebox " + versionNum + "] Hi-C Map ";
// MainWindow variables
public static final Color RULER_LINE_COLOR = new Color(0, 0, 230, 100);
public static final Color DARKULA_RULER_LINE_COLOR = new Color(200, 200, 250, 100);
// for plotting
public static final String topChromosomeColor = "#0000FF";
public static final String leftChromosomeColor = "#009900";
public static final Color backgroundColor = new Color(204, 204, 204);
public static final String BACKUP_FILE_STEM = "unsaved_hic_annotations_backup_";
// for state saving
public static File stateFile;
public static File xmlSavedStatesFile;
// Feature2D hover text
public static final boolean allowSpacingBetweenFeatureText = true;
public static final ArrayList savedStatesList = new ArrayList<>();
// min hic file version supported
public static final int minVersion = 6;
public static final int writingVersion = 9;
public static final int bufferSize = 2097152;
public static final String defaultPropertiesURL = "http://hicfiles.tc4ga.com/juicebox.properties";
public static final Color diffGrayColor = new Color(238, 238, 238);
// for state saving
public static int MAX_PEARSON_ZOOM = 50000;
public static int MAX_EIGENVECTOR_ZOOM = 250000;
// implement Map scaling with this global variable
public static double hicMapScale = 1;
// whether MatrixZoomData should cache or not
public static boolean useCache = true;
public static boolean guiIsCurrentlyActive = false;
public static boolean allowDynamicBlockIndex = true;
public static boolean printVerboseComments = false;
public static boolean slideshowEnabled = false;
public static boolean splitModeEnabled = false;
public static boolean translationInProgress = false;
public static boolean displayTiles = false;
public static boolean isDarkulaModeEnabled = false;
public static boolean isAssemblyMatCheck = false;
// whether instance was linked before mouse press or not
public static boolean phasing = false;
public static boolean noSortInPhasing = false;
public static boolean wasLinkedBeforeMousePress = false;
public static boolean isLegacyOutputPrintingEnabled = false;
public static final boolean isDevAssemblyToolsAllowedPublic = true;
public static final boolean isDevCustomChromosomesAllowedPublic = true;
public static boolean HACK_COLORSCALE = false;
public static boolean HACK_COLORSCALE_EQUAL = false;
public static boolean HACK_COLORSCALE_LINEAR = false;
// for norm/pre, save contact records into memory
public static boolean USE_ITERATOR_NOT_ALL_IN_RAM = false;
public static boolean CHECK_RAM_USAGE = false;
public static void verifySupportedHiCFileVersion(int version) throws RuntimeException {
if (version < minVersion) {
throw new RuntimeException("This file is version " + version +
". Only versions " + minVersion + " and greater are supported at this time.");
}
}
public static void verifySupportedHiCFileWritingVersion(int version) throws RuntimeException {
if (version < writingVersion) {
throw new RuntimeException("This file is version " + version +
". Only versions " + writingVersion + " and greater can be edited using this jar.");
}
}
public static Font font(int size, boolean isBold) {
if (isBold)
return new Font("Arial", Font.BOLD, size);
return new Font("Arial", Font.PLAIN, size);
}
public static int getIdealThreadCount() {
return Math.max(1, Runtime.getRuntime().availableProcessors());
}
public static ExecutorService newFixedThreadPool() {
return Executors.newFixedThreadPool(getIdealThreadCount());
}
public enum menuType {MAP, LOCATION, STATE}
}
================================================
FILE: src/juicebox/IGVUtils.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2020 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package juicebox;
import juicebox.data.ChromosomeHandler;
import org.broad.igv.ui.IGV;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//import org.broad.igv.lists.GeneList;
//import org.broad.igv.ui.WaitCursorManager;
//import org.broad.igv.util.LongRunningTask;
//import org.broad.igv.util.NamedRunnable;
//import java.net.UnknownHostException;
/**
* @author Jim Robinson
* @since 1/5/12
*/
class IGVUtils {
private static final ExecutorService threadExecutor = Executors.newFixedThreadPool(1);
private static SocketHelper helper = null;
private static void createSocketHelper() {
if (helper == null) {
Socket socket;
PrintWriter out;
BufferedReader in;
try {
socket = new Socket("127.0.0.1", 60151);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()), HiCGlobals.bufferSize);
helper = new SocketHelper(in, out, socket);
} catch (IOException e) {
System.err.println("IOException" + e.getLocalizedMessage());
helper = null;
}
}
}
/**
* Send instructions to IGV to open or adjust views on the 2 loci.
*
* @param locus1
* @param locus2
*/
public static void sendToIGV(final String locus1, final String locus2) {
Runnable runnable = new Runnable() {
public void run() {
if (ChromosomeHandler.isAllByAll(locus1)) {
return;
}
// Same JVM?
if (IGV.hasInstance()) {
IGV.getInstance().goToLociList(Arrays.asList(locus1, locus2));
} else {
if (helper == null) createSocketHelper();
if (helper != null) {
String cmd = "gotoimmediate " + locus1 + " " + locus2;
helper.out.println(cmd);
String response = null;
try {
response = helper.in.readLine();
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
System.out.println(cmd + " " + response);
}
}
}
};
threadExecutor.submit(runnable);
}
static class SocketHelper {
Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
SocketHelper(BufferedReader in, PrintWriter out, Socket socket) {
this.in = in;
this.out = out;
this.socket = socket;
}
}
}
================================================
FILE: src/juicebox/MainWindow.java
================================================
/*
* The MIT License (MIT)
*
* Copyright (c) 2011-2021 Broad Institute, Aiden Lab, Rice University, Baylor College of Medicine
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package juicebox;
import juicebox.gui.MainViewPanel;
import juicebox.gui.SuperAdapter;
import juicebox.windowui.DisabledGlassPane;
import juicebox.windowui.FileDropTargetListener;
import juicebox.windowui.layers.LayersPanel;
import org.broad.igv.Globals;
import org.broad.igv.ui.util.IconFactory;
import javax.swing.*;
import java.awt.*;
import java.awt.dnd.DropTarget;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MainWindow extends JFrame {
private static final long serialVersionUID = -9000020;
private static final DisabledGlassPane disabledGlassPane = new DisabledGlassPane(Cursor.WAIT_CURSOR);
private static final SuperAdapter superAdapter = new SuperAdapter();
public static Cursor fistCursor;
public static Cursor pasteNECursor;
public static Cursor pasteSWCursor;
public static Cursor pasteSECursor;
public static Cursor pasteNWCursor;
public static Cursor invertNECursor;
public static Cursor invertSWCursor;
public static Cursor scissorCursor;
public static Cursor groupNECursor;
public static Cursor groupSWCursor;
private static MainWindow theInstance;
private final ExecutorService threadExecutor = Executors.newFixedThreadPool(3);
private final HiC hic; // The "model" object containing the state for this instance.
private MainWindow() {
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
HiCGlobals.guiIsCurrentlyActive = true;
hic = new HiC(superAdapter);
MainViewPanel mainViewPanel = new MainViewPanel();
superAdapter.setAdapters(this, hic, mainViewPanel);
initComponents();
createCursors();
setExtendedState(JFrame.MAXIMIZED_BOTH);
pack();
DropTarget target = new DropTarget(this, new FileDropTargetListener(superAdapter));
setDropTarget(target);
// Tooltip settings
ToolTipManager.sharedInstance().setDismissDelay(60000); // 60 seconds
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(superAdapter.getNewHiCKeyDispatcher());
}
private static MainWindow createMainWindow() {
return new MainWindow();
}
public static synchronized MainWindow getInstance() {
if (theInstance == null) {
try {
theInstance = createMainWindow();
} catch (Exception e) {
System.err.println("Error creating main window " + e.getLocalizedMessage());
}
}
return theInstance;
}
public static void main(String[] args) {
initApplication();
// Runnable runnable = new Runnable() {
// public void run() {
theInstance = getInstance();
theInstance.setVisible(true);
theInstance.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
CommandListener.start(theInstance.hic);
// }
// };
// SwingUtilities.invokeAndWait(runnable);
try {
URL url = new URL("https://s3.amazonaws.com/hicfiles.tc4ga.com/juicebox.version");
URLConnection next = url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(next.getInputStream()));
String latestVersion = reader.readLine();
String[] latest = latestVersion.split("\\.");
String[] current = HiCGlobals.versionNum.split("\\.");
boolean isOutdated = false;
int iC = Integer.parseInt(current[0]);
int iL = Integer.parseInt(latest[0]);
if (iC < iL) {
isOutdated = true;
} else if (iC == iL) {
int jC = Integer.parseInt(current[1]);
int jL = Integer.parseInt(latest[1]);
int kC = Integer.parseInt(current[2]);
int kL = Integer.parseInt(latest[2]);
if (jC < jL) {
isOutdated = true;
} else if (jC == jL && kC < kL) {
isOutdated = true;
}
}
if (isOutdated) {
JPanel textPanel = new JPanel(new GridLayout(0, 1));
JLabel label = new JLabel("
You are using Juicebox " + HiCGlobals.versionNum + " The lastest version is "
+ latestVersion + " To download the lastest version, go to
");
JLabel label2 = new JLabel(" https://github.com/theaidenlab/juicebox/wiki/Download ");
textPanel.add(label);
textPanel.add(label2);
label2.setCursor(new Cursor(Cursor.HAND_CURSOR));
label2.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
try {
Desktop.getDesktop().browse(new URI("https://github.com/theaidenlab/juicebox/wiki/Download"));
} catch (URISyntaxException | IOException ex) {
//It looks like there's a problem
}
}
});
JOptionPane.showMessageDialog(superAdapter.getMainWindow(), textPanel, "Update Information", JOptionPane.PLAIN_MESSAGE);
}
} catch (Exception e) {
System.err.println(e.getLocalizedMessage());
}
}
private static void initApplication() {
System.err.println("Default User Directory: " + DirectoryManager.getUserDirectory());
try {
HiCGlobals.stateFile = new File(DirectoryManager.getHiCDirectory(), "CurrentJuiceboxStates");
HiCGlobals.xmlSavedStatesFile = new File(DirectoryManager.getHiCDirectory(),
"JuiceboxStatesForExport.xml");
} catch (Exception e) {
System.err.println(e.getLocalizedMessage());
if (HiCGlobals.guiIsCurrentlyActive) {
SuperAdapter.showMessageDialog("Error with state file\n" + e.getLocalizedMessage());
}
}
System.setProperty("http.agent", Globals.applicationString());
}
private void initComponents() {
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
superAdapter.exitActionPerformed();
}
});
if (HiCGlobals.printVerboseComments) {
System.out.println("Initializing Components");
}
// first annotation layer must get created
MainWindow.superAdapter.createNewLayer(null);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration());
int taskBarHeight = scnMax.bottom;
MainWindow.superAdapter.initializeMainView(getContentPane(), screenSize, taskBarHeight);
initializeGlassPaneListening();
ImageIcon icon = new ImageIcon(getClass().getResource("/images/juicebox256.png"));
setIconImage(icon.getImage());
}
private void createCursors() {
boolean isWindows = (System.getProperty("os.name").toLowerCase().contains("win"));
// Make background transparent
BufferedImage handImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = handImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
// Draw hand image in middle
g = handImage.createGraphics();
g.drawImage(IconFactory.getInstance().getIcon(IconFactory.IconID.FIST).getImage(), 0, 0, null);
fistCursor = getToolkit().createCustomCursor(handImage, new Point(8, 6), "Move");
// Additional cursors for assembly
ImageIcon imageIcon;
// Insert (paste) prompts
BufferedImage pasteNEImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = pasteNEImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = pasteNEImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/assembly/small-ne-paste.png"), "paste");
g.drawImage(imageIcon.getImage(), 0, 0, null);
if (isWindows) {
pasteNEImage = windowsCreateCursor(pasteNEImage, 240);
}
pasteNECursor = getToolkit().createCustomCursor(pasteNEImage, new Point(8, 6), "PasteNE");
// Insert (paste) prompts
BufferedImage pasteSWImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = pasteSWImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = pasteSWImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/assembly/small-sw-paste.png"), "paste");
g.drawImage(imageIcon.getImage(), 0, 0, null);
if (isWindows) {
pasteSWImage = windowsCreateCursor(pasteSWImage, 240);
}
pasteSWCursor = getToolkit().createCustomCursor(pasteSWImage, new Point(8, 6), "PasteSW");
// Insert (paste) prompts
BufferedImage pasteNWImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = pasteNWImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = pasteNWImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/assembly/small-nw-paste.png"), "paste");
g.drawImage(imageIcon.getImage(), 0, 0, null);
if (isWindows) {
pasteNWImage = windowsCreateCursor(pasteNWImage, 240);
}
pasteNWCursor = getToolkit().createCustomCursor(pasteNWImage, new Point(8, 6), "PasteNW");
// Insert (paste) prompts
BufferedImage pasteSEImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = pasteSEImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = pasteSEImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/assembly/small-se-paste.png"), "paste");
g.drawImage(imageIcon.getImage(), 0, 0, null);
if (isWindows) {
pasteSEImage = windowsCreateCursor(pasteSEImage, 240);
}
pasteSECursor = getToolkit().createCustomCursor(pasteSEImage, new Point(8, 6), "PasteSE");
// Invert prompts
BufferedImage invertNEImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = invertNEImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
g.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = invertNEImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/assembly/small-ne-invert.png"), "invert");
g.drawImage(imageIcon.getImage(), 0, 0, null);
if (isWindows) {
invertNEImage = windowsCreateCursor(invertNEImage, 240);
}
invertNECursor = getToolkit().createCustomCursor(invertNEImage, new Point(8, 6), "InvertNE");
// Invert prompts
BufferedImage invertSWImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = invertSWImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = invertSWImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/assembly/small-sw-invert.png"), "invert");
g.drawImage(imageIcon.getImage(), 0, 0, null);
if (isWindows) {
invertSWImage = windowsCreateCursor(invertSWImage, 240);
}
invertSWCursor = getToolkit().createCustomCursor(invertSWImage, new Point(8, 6), "InvertSW");
// Cut prompts
BufferedImage scissorImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = scissorImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = scissorImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/assembly/small-scissors.png"), "cut");
g.drawImage(imageIcon.getImage(), 0, 0, null);
if (isWindows) {
scissorImage = windowsCreateCursor(scissorImage, 141);
}
scissorCursor = getToolkit().createCustomCursor(scissorImage, new Point(8, 6), "Scissors");
// Group prompts
BufferedImage groupNEImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = groupNEImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
g.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = groupNEImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/layer/ur_clicked.png"), "grouptoggle");
g.drawImage(imageIcon.getImage(), 0, 0, 20, 20, null);
groupNECursor = getToolkit().createCustomCursor(groupNEImage, new Point(8, 6), "GroupNE");
BufferedImage groupSWImage = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
g = groupSWImage.createGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
g.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
rect = new Rectangle2D.Double(0, 0, 32, 32);
g.fill(rect);
g = groupSWImage.createGraphics();
imageIcon = new ImageIcon(this.getClass().getResource("/images/layer/ll_clicked.png"), "grouptoggle");
g.drawImage(imageIcon.getImage(), 0, 0, 20, 20, null);
groupSWCursor = getToolkit().createCustomCursor(groupSWImage, new Point(8, 6), "GroupSW");
}
private BufferedImage windowsCreateCursor(BufferedImage img, int thresholdVal) {
try {
int size = 32;
BufferedImage image = new BufferedImage(size, size,
BufferedImage.TYPE_INT_RGB);
BufferedImage image2 = new BufferedImage(size, size,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
Graphics2D g2 = image2.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, size, size);
// turn on anti-aliasing.
g.setStroke(new BasicStroke(4.0f)); // 4-pixel lines
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setColor(new Color(0.5f, 0f, 0f));
g.drawImage(img, 0, 0, null, null);
g2.drawImage(image, 0, 0, null, null);
for (int y = 0 ; y < size ; y++) {
for (int x = 0 ; x < size ; x++) {
int rgb = image.getRGB(x, y);
int blue = rgb & 0xff;
int green = (rgb & 0xff00) >> 8;
int red = (rgb & 0xff0000) >> 16;
if (red >= thresholdVal && green >= thresholdVal && blue >= thresholdVal) {
// make white transparent
image2.setRGB(x, y, 0);
}
}
}
return image2;
}
catch (Exception exp) {
exp.printStackTrace();
return null;
}
}
public void exitActionPerformed() {
int option = 0;
if (SuperAdapter.assemblyModeCurrentlyActive) {
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
option = JOptionPane.showConfirmDialog(null, "Are you sure you want to exit? Make sure you have saved any important assembly changes.",
"Warning", JOptionPane.YES_NO_OPTION);
}
if (option == 0) {
setVisible(false);
dispose();
String autoSaveFileName = DirectoryManager.getHiCDirectory() + "/" +
System.nanoTime() + ".review.autosave.assembly";
try {
autoSaveFileName = DirectoryManager.getHiCDirectory() + "/" +
(SuperAdapter.getDatasetTitle().split(".+?/(?=[^/]+$)")[1]).split("\\.(?=[^\\.]+$)")[0] +
".review.autosave.assembly";
} catch (Exception e) {
System.err.println("Unable to get desired file name");
System.err.println(e.getLocalizedMessage());
}
File autoSaveFile = new File(autoSaveFileName);
autoSaveFile.delete();
System.out.println("Exiting Main Window");
System.exit(0);
}
}
/**
* Utility function to execute a task in a worker thread. The method is on MainWindow because the glassPane
* is used to display a wait cursor and block events.
*
* @param runnable Thread
* @return thread
*/
public Future> executeLongRunningTask(final Runnable runnable, final String caller) {
return executeLongRunningTask(runnable, caller, "Loading...");
}
public Future> executeLongRunningTask(final Runnable runnable, final String caller, final String message) {
if (HiCGlobals.printVerboseComments) {
System.out.println("long_execute " + caller);
}
Callable