public static class

SourceModel.Line

extends Object
/*
 * Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.example.debug.gui;

import java.io.*;
import java.util.*;

import com.sun.jdi.*;
import com.sun.jdi.request.*;

import com.sun.tools.example.debug.bdi.*;

import javax.swing.*;

/**
 * Represents and manages one source file.
 * Caches source lines.  Holds other source file info.
 */
public class SourceModel extends AbstractListModel {

    private File path;

    boolean isActuallySource = true;

    private List<ReferenceType> classes = new ArrayList<ReferenceType>();

    private Environment env;

    // Cached line-by-line access.

    //### Unify this with source model used in source view?
    //### What is our cache-management policy for these?
    //### Even with weak refs, we won't discard any part of the
    //### source if the SourceModel object is reachable.
    /**
     * List of Line.
     */
    private List<Line> sourceLines = null;

    public static class Line {
        public String text;
        public boolean hasBreakpoint = false;
        public ReferenceType refType = null;
        Line(String text) {
            this.text = text;
        }
        public boolean isExecutable() {
            return refType != null;
        }
        public boolean hasBreakpoint() {
            return hasBreakpoint;
        }
    };

    // 132 characters long, all printable characters.
    public static final Line prototypeCellValue = new Line(
                                        "abcdefghijklmnopqrstuvwxyz" +
                                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                                        "1234567890~!@#$%^&*()_+{}|" +
                                        ":<>?`-=[];',.XXXXXXXXXXXX/\\\"");

    SourceModel(Environment env, File path) {
        this.env = env;
        this.path = path;
    }

    public SourceModel(String message) {
        this.path = null;
        setMessage(message);
    }

    private void setMessage(String message) {
        isActuallySource = false;
        sourceLines = new ArrayList<Line>();
        sourceLines.add(new Line(message));
    }

    // **** Implement ListModel  *****

    public Object getElementAt(int index) {
        if (sourceLines == null) {
            initialize();
        }
        return sourceLines.get(index);
    }

    public int getSize() {
        if (sourceLines == null) {
            initialize();
        }
        return sourceLines.size();
    }

    // ***** Other functionality *****

    public File fileName() {
        return path;
    }

    public BufferedReader sourceReader() throws IOException {
        return new BufferedReader(new FileReader(path));
    }

    public Line line(int lineNo) {
        if (sourceLines == null) {
            initialize();
        }
        int index = lineNo - 1; // list is 0-indexed
        if (index >= sourceLines.size() || index < 0) {
            return null;
        } else {
            return sourceLines.get(index);
        }
    }

    public String sourceLine(int lineNo) {
        Line line = line(lineNo);
        if (line == null) {
            return null;
        } else {
            return line.text;
        }
    }

    void addClass(ReferenceType refType) {
        // Logically is Set
        if (classes.indexOf(refType) == -1) {
            classes.add(refType);
            if (sourceLines != null) {
                markClassLines(refType);
            }
        }
    }

    /**
     * @return List of currently known {@link com.sun.jdi.ReferenceType}
     * in this source file.
     */
    public List<ReferenceType> referenceTypes() {
        return Collections.unmodifiableList(classes);
    }

    private void initialize() {
        try {
            rawInit();
        } catch (IOException exc) {
            setMessage("[Error reading source code]");
        }
    }

    public void showBreakpoint(int ln, boolean hasBreakpoint) {
        line(ln).hasBreakpoint = hasBreakpoint;
        fireContentsChanged(this, ln, ln);
    }

    public void showExecutable(int ln, ReferenceType refType) {
        line(ln).refType = refType;
        fireContentsChanged(this, ln, ln);
    }

    /**
     * Mark executable lines and breakpoints, but only
     * when sourceLines is set.
     */
    private void markClassLines(ReferenceType refType) {
        List methods = refType.methods();
        for (Iterator mit = methods.iterator(); mit.hasNext();) {
            Method meth = (Method)mit.next();
            try {
                List lines = meth.allLineLocations();
                for (Iterator lit = lines.iterator(); lit.hasNext();) {
                    Location loc = (Location)lit.next();
                    showExecutable(loc.lineNumber(), refType);
                }
            } catch (AbsentInformationException exc) {
                // do nothing
            }
        }
        List bps = env.getExecutionManager().eventRequestManager().breakpointRequests();
        for (Iterator it = bps.iterator(); it.hasNext();) {
            BreakpointRequest bp = (BreakpointRequest)it.next();
            if (bp.location() != null) {
                Location loc = bp.location();
                if (loc.declaringType().equals(refType)) {
                    showBreakpoint(loc.lineNumber(),true);
                }
            }
        }
    }

    private void rawInit() throws IOException {
        sourceLines = new ArrayList<Line>();
        BufferedReader reader = sourceReader();
        try {
            String line = reader.readLine();
            while (line != null) {
                sourceLines.add(new Line(expandTabs(line)));
                line = reader.readLine();
            }
        } finally {
            reader.close();
        }
        for (Iterator it = classes.iterator(); it.hasNext();) {
            markClassLines((ClassType)it.next());
        }
    }

    private String expandTabs(String s) {
        int col = 0;
        int len = s.length();
        StringBuffer sb = new StringBuffer(132);
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            sb.append(c);
            if (c == '\t') {
                int pad = (8 - (col % 8));
                for (int j = 0; j < pad; j++) {
                    sb.append(' ');
                }
                col += pad;
            } else {
                col++;
            }
        }
        return sb.toString();
    }

}