Java tutorial
/** * Copyright 2016 Iron City Software LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package garmintools.sections; import garmintools.Proto; import garmintools.adapters.garmin.GarminOutput; import garmintools.wrappers.TableOfContentsEntry; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ContiguousSet; import com.google.common.collect.DiscreteDomain; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Range; public class SectionManager { // This list is used when the proto does not specify an ordering. // This list can probably be dynamically calculated if we had a foreign key dependency graph. private static final int[] DEFAULT_SECTION_ORDER = new int[] { 12000, 12001, 0, 74, 43, 75, 27, 89, 1, 2, 86, 10, 42, 85, 90, 91, 92, 93, 94, 41, 62, 55, 56, 57, 58, 70, 64, 71, 49, 52, 48, 47, 50, 51, 67, 59, 60, 54, 53, 76, 77, 79, 80, 81, 87, 78, 35, 34, 33, 31, 30, 32, 14, 12, 11, 13, 18, 16, 15, 17, 19, 20, 24, 22, 21, 23, 25, 26, 9, 6, 4, 3, 5, 7, 8, 69, 29, 28, 40, 37, 36, 38, 39, 68, 88, 45, 44, 46, 73, 72, 65 }; // TODO: this is the work to do :-( private static final int[] UNPARSED_SECTIONS = new int[] { 4, 5, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 50, 51, 52, 56, 57, 58, 61, 63, 65, 66, 68, 69, 71, 72, 73, 74, 75, 78, 82, 83, 84, 85, 86, 88, 89, 90, 91, 92, 93, 94 }; private final Map<Integer, Section<?>> sections; private final AirspaceTable airspaceTable; private final RunwayNumberSuffixTable runwayNumberSuffixTable; private SectionManager(List<Section<?>> sections, AirspaceTable airspaceTable, RunwayNumberSuffixTable runwayNumberSuffixTable) { ImmutableMap.Builder<Integer, Section<?>> mapBuilder = ImmutableMap.builder(); for (Section<?> section : sections) { mapBuilder.put(section.sectionNumber, section); } this.sections = mapBuilder.build(); this.airspaceTable = new AirspaceTable(); this.runwayNumberSuffixTable = new RunwayNumberSuffixTable(); } public AirspaceTable getAirspaceTable() { return airspaceTable; } public RunwayNumberSuffixTable getRunwayNumberSuffixTable() { return runwayNumberSuffixTable; } public DataLengthSection getDataLengthSection() { return (DataLengthSection) sections.get(Ids.DATA_LENGTH_SECTION); } public IcaoRegionSection getIcaoRegionSection() { return (IcaoRegionSection) sections.get(Ids.ICAO_REGION_SECTION); } public StringSection getStringSection() { return (StringSection) sections.get(Ids.STRING_SECTION); } public LandingFacilityIdentifierIndexSection getLandingFacilityIdentifierIndexSection() { return (LandingFacilityIdentifierIndexSection) sections.get(Ids.LANDING_FACILITY_IDENTIFIER_INDEX_SECTION); } public LandingFacilityDetailSection getLandingFacilityDetailSection() { return (LandingFacilityDetailSection) sections.get(Ids.LANDING_FACILITY_DETAIL_SECTION); } public LookupTableSection getRunwayLightingSection() { return (LookupTableSection) sections.get(Ids.RUNWAY_LIGHTING_SECTION); } public LookupTableSection getRunwaySurfaceSection() { return (LookupTableSection) sections.get(Ids.RUNWAY_SURFACE_SECTION); } public LookupTableSection getGenericAirportStringSection1() { return (LookupTableSection) sections.get(Ids.GENERIC_AIRPORT_STRING_SECTION1); } public LookupTableSection getLandingFacilityTypeSection() { return (LookupTableSection) sections.get(Ids.LANDING_FACILITY_TYPE_SECTION); } public LookupTableSection getGenericAirportStringSection2() { return (LookupTableSection) sections.get(Ids.GENERIC_AIRPORT_STRING_SECTION2); } public TableOfContentsSection getTableOfContentsSection() { return (TableOfContentsSection) sections.get(Ids.TABLE_OF_CONTENTS_SECTION); } public MetadataSection getMetadataSection() { return (MetadataSection) sections.get(Ids.METADATA_SECTION); } public void mergeFromProto(Proto.NavigationData proto) { for (Section<?> section : sections.values()) { section.mergeFromProto(this, proto); } } public void mergeToProto(Proto.NavigationData.Builder protoBuilder) { for (Section<?> section : sections.values()) { section.mergeToProto(this, protoBuilder); } } public Map<Integer, GarminOutput> getGarminOutputs() { ImmutableMap.Builder<Integer, GarminOutput> sectionToBufferBuilder = ImmutableMap.builder(); for (int sectionNumber : DEFAULT_SECTION_ORDER) { if (sectionNumber <= Ids.MAX_SECTION_NUMBER) { sectionToBufferBuilder.put(sectionNumber, sections.get(sectionNumber).getSectionBytes(this)); } } return sectionToBufferBuilder.build(); } private static final List<SectionFactory<?>> SECTION_FACTORIES_LIST = ImmutableList.<SectionFactory<?>>of( new MetadataSection.Factory(), new TableOfContentsSection.Factory(), new DataLengthSection.Factory(), new CoordinateSystemSection.Factory(), new IcaoRegionSection.Factory(), new StringSection.Factory(), new LandingFacilityIdentifierIndexSection.Factory(), new LandingFacilitySection.Factory(), new LandingFacilityDetailSection.Factory(), new NavigationFixSection.Factory(), new LookupTableSection.Factory(Ids.RUNWAY_LIGHTING_SECTION), new LookupTableSection.Factory(Ids.RUNWAY_SURFACE_SECTION), new LookupTableSection.Factory(Ids.GENERIC_AIRPORT_STRING_SECTION1), new LookupTableSection.Factory(Ids.LHTU_SECTION), new LookupTableSection.Factory(Ids.APPROACH_TYPE_SECTION), new LookupTableSection.Factory(Ids.LANDING_FACILITY_TYPE_SECTION), new LookupTableSection.Factory(Ids.AIRSPACE_ABBREVIATION_SECTION1), new LookupTableSection.Factory(Ids.AIRSPACE_ABBREVIATION_SECTION2), new LookupTableSection.Factory(Ids.INSTRUMENT_APPROACH_TYPE_SECTION), new LookupTableSection.Factory(Ids.NESTB_SECTION), new LookupTableSection.Factory(Ids.BRIEF_AIRSPACE_TYPE_SECTION1), new LookupTableSection.Factory(Ids.RNAV_TYPE_SECTION), new LookupTableSection.Factory(Ids.GPS_APPROACH_TYPE_SECTION1), new LookupTableSection.Factory(Ids.GPS_APPROACH_TYPE_SECTION2), new LookupTableSection.Factory(Ids.GENERIC_AIRPORT_STRING_SECTION2), new LookupTableSection.Factory(Ids.EXPANDED_AIRSPACE_ABBREVIATION_SECTION1), new LookupTableSection.Factory(Ids.EXPANDED_AIRSPACE_ABBREVIATION_SECTION2), new LookupTableSection.Factory(Ids.BRIEF_AIRSPACE_TYPE_SECTION2)); private static final Map<Integer, SectionFactory<?>> SECTION_FACTORIES = createSectionFactories(); private static Map<Integer, SectionFactory<?>> createSectionFactories() { Set<Integer> allIds = new HashSet<>( ContiguousSet.create(Range.closed(0, Ids.MAX_SECTION_NUMBER), DiscreteDomain.integers())); ImmutableMap.Builder<Integer, SectionFactory<?>> builder = ImmutableMap.builder(); for (SectionFactory<?> sectionFactory : SECTION_FACTORIES_LIST) { builder.put(sectionFactory.getSectionNumber(), sectionFactory); allIds.remove(sectionFactory.getSectionNumber()); } for (int unparsedSectionNumber : UNPARSED_SECTIONS) { builder.put(unparsedSectionNumber, new UnparsedSection.Factory(unparsedSectionNumber)); allIds.remove(unparsedSectionNumber); } Preconditions.checkState(allIds.isEmpty(), "Unbound section(s): " + allIds); return builder.build(); } public static class GarminBuilder { private List<Section<?>> sections = new ArrayList<>(); public GarminBuilder readTableOfContents(InputStream inputStream, int inputFileLength) throws IOException { TableOfContentsSection.Factory factory = (TableOfContentsSection.Factory) SECTION_FACTORIES .get(Ids.TABLE_OF_CONTENTS_SECTION); sections.add(factory.createFromGarmin(inputStream, inputFileLength)); return this; } public GarminBuilder addSection(TableOfContentsEntry entry, ByteBuffer byteBuffer) { Section<?> section = SECTION_FACTORIES.get(entry.sectionNumber) .createFromGarmin((DataLengthSection) getSection(Ids.DATA_LENGTH_SECTION), entry, byteBuffer); sections.add(section); return this; } public Section<?> getSection(int sectionNumber) { for (Section<?> section : sections) { if (section.sectionNumber == sectionNumber) { return section; } } return null; } public SectionManager build() { return new SectionManager(sections, new AirspaceTable(), new RunwayNumberSuffixTable()); } } public static class ProtoBuilder { private final Proto.NavigationData proto; private final List<Section<?>> sections; public ProtoBuilder(Proto.NavigationData proto) { this.proto = proto; this.sections = new ArrayList<>(); } public SectionManager build() { for (int sectionNumber : DEFAULT_SECTION_ORDER) { // proto.getMetadata().getSectionList()) { SectionFactory<?> sectionFactory = SECTION_FACTORIES.get(sectionNumber); // System.out.println("Reading section " + sectionNumber); Section<?> section = sectionFactory.createFromProto(proto); // first pass sections.add(section); } SectionManager sectionManager = new SectionManager(sections, new AirspaceTable(), new RunwayNumberSuffixTable()); sectionManager.mergeFromProto(proto); // second pass return sectionManager; } } @Override public String toString() { List<String> strings = new ArrayList<>(); for (int sectionNumber : sections.keySet()) { strings.add(String.format(">>> section %2d\n%s\n<<< section %2d", sectionNumber, sections.get(sectionNumber), sectionNumber)); } return Joiner.on('\n').join(strings); } }