class SettingsHelper {

    private final IndexReader reader;

    /**
     * Key is Project name or empty string for null Project.
     */
    private Map<String, IndexAnalysisSettings3> mappedAnalysisSettings;

    /**
     * Key is Project name or empty string for null Project. Map is ordered by
     * canonical length (ASC) and then canonical value (ASC).
     */
    private Map<String, Map<String, IndexedSymlink>> mappedIndexedSymlinks;

    public SettingsHelper(IndexReader reader) {
        if (reader == null) {
            throw new IllegalArgumentException("reader is null");
        }
        this.reader = reader;
    }

    /**
     * Gets any mapped symlinks (after having called {@link #getSettings(String)}).
     * @return either a defined map or {@code null}
     */
    public Map<String, IndexedSymlink> getSymlinks(String projectName) throws IOException {
        getSettings(projectName);
        String projectKey = projectName != null ? projectName : "";
        Map<String, IndexedSymlink> indexSymlinks = mappedIndexedSymlinks.get(projectKey);
        return Optional.ofNullable(indexSymlinks)
                .map(Collections::unmodifiableMap)
                .orElseGet(Collections::emptyMap);
    }

    /**
     * Gets the persisted tabSize via {@link #getSettings(String)} if
     * available or returns the {@code proj} tabSize if available -- or zero.
     * @param proj a defined instance or {@code null} if no project is active
     * @return tabSize
     * @throws IOException if an I/O error occurs querying the initialized
     * reader
     */
    public int getTabSize(Project proj) throws IOException {<FILL_FUNCTION_BODY>}

    /**
     * Gets the settings for a specified project.
     * @param projectName a defined instance or {@code null} if no project is
     * active (or empty string to mean the same thing)
     * @return a defined instance or {@code null} if none is found
     * @throws IOException if an I/O error occurs querying the initialized reader
     */
    public IndexAnalysisSettings3 getSettings(String projectName) throws IOException {
        if (mappedAnalysisSettings == null) {
            IndexAnalysisSettingsAccessor dao = new IndexAnalysisSettingsAccessor();
            IndexAnalysisSettings3[] setts = dao.read(reader, Short.MAX_VALUE);
            map(setts);
        }

        String projectKey = projectName != null ? projectName : "";
        return mappedAnalysisSettings.get(projectKey);
    }

    private void map(IndexAnalysisSettings3[] setts) {

        Map<String, IndexAnalysisSettings3> settingsMap = new HashMap<>();
        Map<String, Map<String, IndexedSymlink>> symlinksMap = new HashMap<>();

        for (IndexAnalysisSettings3 settings : setts) {
            String projectName = settings.getProjectName();
            String projectKey = projectName != null ? projectName : "";
            settingsMap.put(projectKey, settings);
            symlinksMap.put(projectKey, mapSymlinks(settings));
        }
        mappedAnalysisSettings = settingsMap;
        mappedIndexedSymlinks = symlinksMap;
    }

    private Map<String, IndexedSymlink> mapSymlinks(IndexAnalysisSettings3 settings) {

        Map<String, IndexedSymlink> res = new TreeMap<>(
                Comparator.comparingInt(String::length).thenComparing(o -> o));
        for (IndexedSymlink entry : settings.getIndexedSymlinks().values()) {
            res.put(entry.getCanonical(), entry);
        }
        return res;
    }
}

        String projectName = proj != null ? proj.getName() : null;
        IndexAnalysisSettings3 settings = getSettings(projectName);
        int tabSize;
        if (settings != null && settings.getTabSize() != null) {
            tabSize = settings.getTabSize();
        } else {
            tabSize = proj != null ? proj.getTabSize() : 0;
        }
        return tabSize;
    
<no_super_class>