garmintools.sections.SectionManager.java Source code

Java tutorial

Introduction

Here is the source code for garmintools.sections.SectionManager.java

Source

/**
 *    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);
    }
}