Task 572: .PRP File Format

Task 572: .PRP File Format

.PRP File Format Specifications

The .PRP file format is a text-based properties file used by IBM Rational Rhapsody, a visual development tool for systems engineers and software developers. It stores settings and configurations for data models in a structured, recursive format using LL1 syntax. The file is human-readable and consists of nested blocks defined by keywords like Subject, Metaclass, Property, value definitions (e.g., Enum), and end to close each block. Comments are supported with //. The format allows overriding default properties from factory.prp with custom ones in site.prp or user-specific files.

1. List of Properties Intrinsic to the File Format

The properties are the structural elements that define the format's syntax and content. These are intrinsic to how the file is parsed and used in the Rhapsody ecosystem:

  • Subject: Top-level category for grouping related properties (e.g., CPP_CG for C++ code generation).
  • Metaclass: Sub-category under Subject, specifying the class type (e.g., Configuration).
  • Property: Specific setting name under Metaclass (e.g., CodeGenerationDirectoryLevel).
  • Type: The data type of the property, such as Enum for enumerated values.
  • Values: The list of possible values for the property (e.g., "Top, Bottom" for an Enum).
  • Default: The default value for the property (e.g., "Bottom").
  • end: Keyword to close each nested block (Subject, Metaclass, Property).
  • Comments: Lines starting with // for documentation or notes.

These elements form a hierarchical structure, allowing recursive parsing.

(Note: Direct .PRP files are often bundled in software installations or docs; these links provide examples in PDF format for extraction.)

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .PRP File Dump

PRP File Dumper
Drag and drop .PRP file here

4. Python Class for .PRP File Handling

class PRPFile:
    def __init__(self, filepath=None):
        self.filepath = filepath
        self.structure = []

    def open(self, filepath):
        self.filepath = filepath
        with open(filepath, 'r') as f:
            self._parse(f.read())

    def _parse(self, text):
        lines = text.split('\n')
        stack = []
        current = {'type': 'root', 'children': []}
        self.structure = current['children']
        for line in lines:
            line = line.strip()
            if line.startswith('//'): continue
            if line == 'end':
                if stack:
                    current = stack.pop()
                continue
            parts = line.split()
            if parts[0] == 'Subject':
                node = {'type': 'Subject', 'name': parts[1], 'children': []}
                current['children'].append(node)
                stack.append(current)
                current = node
            elif parts[0] == 'Metaclass':
                node = {'type': 'Metaclass', 'name': parts[1], 'children': []}
                current['children'].append(node)
                stack.append(current)
                current = node
            elif parts[0] == 'Property':
                node = {'type': 'Property', 'name': parts[1], 'children': []}
                current['children'].append(node)
                stack.append(current)
                current = node
            elif parts[0] == 'Enum':
                values = parts[1].strip('"').split(',')
                default = parts[2].strip('"') if len(parts) > 2 else ''
                current['children'].append({'type': 'Enum', 'values': values, 'default': default})

    def print_properties(self, structure=None, indent=0):
        if structure is None:
            structure = self.structure
        for prop in structure:
            print('  ' * indent + f"{prop['type']}: {prop.get('name', '')}")
            if 'values' in prop:
                print('  ' * (indent + 1) + f"Values: {', '.join(prop['values'])}")
            if 'default' in prop:
                print('  ' * (indent + 1) + f"Default: {prop['default']}")
            if 'children' in prop:
                self.print_properties(prop['children'], indent + 1)

    def write(self, filepath=None):
        if not filepath:
            filepath = self.filepath
        with open(filepath, 'w') as f:
            f.write(self._serialize(self.structure))

    def _serialize(self, structure, indent=0):
        str_out = ''
        for prop in structure:
            if prop['type'] == 'Subject':
                str_out += '  ' * indent + f"Subject {prop['name']}\n"
            elif prop['type'] == 'Metaclass':
                str_out += '  ' * indent + f"Metaclass {prop['name']}\n"
            elif prop['type'] == 'Property':
                str_out += '  ' * indent + f"Property {prop['name']}\n"
            if 'children' in prop:
                str_out += self._serialize(prop['children'], indent + 1)
            if 'type' in prop and prop['type'] == 'Enum':
                str_out += '  ' * indent + f"Enum \"{','.join(prop['values'])}\", \"{prop['default']}\"\n"
            if 'type' in prop and prop['type'] != 'Enum':
                str_out += '  ' * indent + 'end\n'
        return str_out

# Example usage
# prp = PRPFile()
# prp.open('example.prp')
# prp.print_properties()
# prp.write('output.prp')

To arrive at the solution: The class parses the text into a tree structure using a stack for nesting. Printing traverses the tree recursively. Writing serializes the tree back to text format.

5. Java Class for .PRP File Handling

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class PRPFile {
    private String filepath;
    private List<Node> structure = new ArrayList<>();

    public PRPFile(String filepath) {
        this.filepath = filepath;
    }

    public void open(String filepath) throws IOException {
        this.filepath = filepath;
        try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) {
            StringBuilder text = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                text.append(line).append("\n");
            }
            parse(text.toString());
        }
    }

    private void parse(String text) {
        String[] lines = text.split("\n");
        Stack<Node> stack = new Stack<>();
        Node current = new Node("root", null);
        structure = current.children;
        for (String line : lines) {
            line = line.trim();
            if (line.startsWith("//")) continue;
            if (line.equals("end")) {
                if (!stack.isEmpty()) current = stack.pop();
                continue;
            }
            String[] parts = line.split("\\s+");
            if (parts.length < 2) continue;
            if (parts[0].equals("Subject")) {
                Node node = new Node("Subject", parts[1]);
                current.children.add(node);
                stack.push(current);
                current = node;
            } else if (parts[0].equals("Metaclass")) {
                Node node = new Node("Metaclass", parts[1]);
                current.children.add(node);
                stack.push(current);
                current = node;
            } else if (parts[0].equals("Property")) {
                Node node = new Node("Property", parts[1]);
                current.children.add(node);
                stack.push(current);
                current = node;
            } else if (parts[0].equals("Enum")) {
                Node node = new Node("Enum", null);
                node.values = parts[1].replace("\"", "").split(",");
                node.defaultValue = parts.length > 2 ? parts[2].replace("\"", "") : "";
                current.children.add(node);
            }
        }
    }

    public void printProperties() {
        printProperties(structure, 0);
    }

    private void printProperties(List<Node> nodes, int indent) {
        for (Node node : nodes) {
            System.out.println("  ".repeat(indent) + node.type + ": " + (node.name != null ? node.name : ""));
            if (node.values != null) {
                System.out.println("  ".repeat(indent + 1) + "Values: " + String.join(", ", node.values));
            }
            if (node.defaultValue != null) {
                System.out.println("  ".repeat(indent + 1) + "Default: " + node.defaultValue);
            }
            printProperties(node.children, indent + 1);
        }
    }

    public void write(String filepath) throws IOException {
        try (FileWriter writer = new FileWriter(filepath)) {
            writer.write(serialize(structure, 0));
        }
    }

    private String serialize(List<Node> nodes, int indent) {
        StringBuilder str = new StringBuilder();
        for (Node node : nodes) {
            if (node.type.equals("Subject")) {
                str.append("  ".repeat(indent)).append("Subject ").append(node.name).append("\n");
            } else if (node.type.equals("Metaclass")) {
                str.append("  ".repeat(indent)).append("Metaclass ").append(node.name).append("\n");
            } else if (node.type.equals("Property")) {
                str.append("  ".repeat(indent)).append("Property ").append(node.name).append("\n");
            }
            str.append(serialize(node.children, indent + 1));
            if (!node.type.equals("Enum")) {
                str.append("  ".repeat(indent)).append("end\n");
            } else {
                str.append("  ".repeat(indent)).append("Enum \"").append(String.join(",", node.values)).append("\", \"").append(node.defaultValue).append("\"\n");
            }
        }
        return str.toString();
    }

    static class Node {
        String type;
        String name;
        String defaultValue;
        String[] values;
        List<Node> children = new ArrayList<>();

        Node(String type, String name) {
            this.type = type;
            this.name = name;
        }
    }

    // Example usage
    // public static void main(String[] args) throws IOException {
    //     PRPFile prp = new PRPFile("example.prp");
    //     prp.open("example.prp");
    //     prp.printProperties();
    //     prp.write("output.prp");
    // }
}

To arrive at the solution: The class uses a Node inner class to build a tree. Parsing uses a stack for nesting. Printing and serializing traverse the tree recursively.

6. JavaScript Class for .PRP File Handling

class PRPFile {
    constructor(filepath = null) {
        this.filepath = filepath;
        this.structure = [];
    }

    open(filepath, callback) {
        this.filepath = filepath;
        const reader = new FileReader();
        reader.onload = (e) => {
            this._parse(e.target.result);
            callback();
        };
        reader.readAsText(new File([], filepath)); // Note: For node, use fs
    }

    _parse(text) {
        const lines = text.split('\n');
        const stack = [];
        let current = { type: 'root', children: [] };
        this.structure = current.children;
        lines.forEach(line => {
            line = line.trim();
            if (line.startsWith('//')) return;
            if (line === 'end') {
                if (stack.length) current = stack.pop();
                return;
            }
            const parts = line.split(/\s+/);
            if (parts[0] === 'Subject') {
                const node = { type: 'Subject', name: parts[1], children: [] };
                current.children.push(node);
                stack.push(current);
                current = node;
            } else if (parts[0] === 'Metaclass') {
                const node = { type: 'Metaclass', name: parts[1], children: [] };
                current.children.push(node);
                stack.push(current);
                current = node;
            } else if (parts[0] === 'Property') {
                const node = { type: 'Property', name: parts[1], children: [] };
                current.children.push(node);
                stack.push(current);
                current = node;
            } else if (parts[0] === 'Enum') {
                const values = parts[1].replace(/"/g, '').split(',');
                const defaultValue = parts[2] ? parts[2].replace(/"/g, '') : '';
                current.children.push({ type: 'Enum', values, default: defaultValue });
            }
        });
    }

    printProperties(structure = this.structure, indent = 0) {
        structure.forEach(prop => {
            console.log('  '.repeat(indent) + `${prop.type}: ${prop.name || ''}`);
            if (prop.values) console.log('  '.repeat(indent + 1) + `Values: ${prop.values.join(', ')}`);
            if (prop.default) console.log('  '.repeat(indent + 1) + `Default: ${prop.default}`);
            if (prop.children) this.printProperties(prop.children, indent + 1);
        });
    }

    write(filepath, callback) {
        const text = this._serialize(this.structure);
        // For browser, use blob; for node, fs.writeFile
        const blob = new Blob([text], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filepath || this.filepath;
        a.click();
        callback();
    }

    _serialize(structure, indent = 0) {
        let str = '';
        structure.forEach(prop => {
            if (prop.type === 'Subject') {
                str += '  '.repeat(indent) + `Subject ${prop.name}\n`;
            } else if (prop.type === 'Metaclass') {
                str += '  '.repeat(indent) + `Metaclass ${prop.name}\n`;
            } else if (prop.type === 'Property') {
                str += '  '.repeat(indent) + `Property ${prop.name}\n`;
            }
            if (prop.children) str += this._serialize(prop.children, indent + 1);
            if (prop.type !== 'Enum') {
                str += '  '.repeat(indent) + 'end\n';
            } else {
                str += '  '.repeat(indent) + `Enum "${prop.values.join(',')}", "${prop.default}"\n`;
            }
        });
        return str;
    }
}

// Example usage
// const prp = new PRPFile();
// prp.open('example.prp', () => {
//     prp.printProperties();
//     prp.write('output.prp', () => console.log('Written'));
// });

7. C Class for .PRP File Handling

(Note: Using C++ for practical class implementation.)

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;

struct Node {
    string type;
    string name;
    string defaultValue;
    vector<string> values;
    vector<Node> children;
};

class PRPFile {
private:
    string filepath;
    vector<Node> structure;

    void parse(const string& text) {
        istringstream iss(text);
        string line;
        vector<Node*> stack;
        Node* current = new Node{"root", "", "", {}, {}};
        structure = current->children;
        while (getline(iss, line)) {
            line = line.substr(0, line.find("//")); // Ignore comments
            istringstream lineIss(line);
            string token;
            vector<string> parts;
            while (lineIss >> token) parts.push_back(token);
            if (parts.empty()) continue;
            if (parts[0] == "end") {
                if (!stack.empty()) {
                    current = stack.back();
                    stack.pop_back();
                }
                continue;
            }
            if (parts[0] == "Subject") {
                Node node = {"Subject", parts[1], "", {}, {}};
                current->children.push_back(node);
                stack.push_back(current);
                current = &current->children.back();
            } else if (parts[0] == "Metaclass") {
                Node node = {"Metaclass", parts[1], "", {}, {}};
                current->children.push_back(node);
                stack.push_back(current);
                current = &current->children.back();
            } else if (parts[0] == "Property") {
                Node node = {"Property", parts[1], "", {}, {}};
                current->children.push_back(node);
                stack.push_back(current);
                current = &current->children.back();
            } else if (parts[0] == "Enum") {
                string vals = parts[1].substr(1, parts[1].length() - 2);
                istringstream valIss(vals);
                string val;
                vector<string> values;
                while (getline(valIss, val, ',')) values.push_back(val);
                string def = parts.size() > 2 ? parts[2].substr(1, parts[2].length() - 2) : "";
                Node node = {"Enum", "", def, values, {}};
                current->children.push_back(node);
            }
        }
    }

    void printProperties(const vector<Node>& nodes, int indent = 0) {
        for (const auto& prop : nodes) {
            for (int i = 0; i < indent; ++i) cout << "  ";
            cout << prop.type << ": " << prop.name << endl;
            if (!prop.values.empty()) {
                for (int i = 0; i < indent + 1; ++i) cout << "  ";
                cout << "Values: ";
                for (size_t j = 0; j < prop.values.size(); ++j) {
                    cout << prop.values[j];
                    if (j < prop.values.size() - 1) cout << ", ";
                }
                cout << endl;
            }
            if (!prop.defaultValue.empty()) {
                for (int i = 0; i < indent + 1; ++i) cout << "  ";
                cout << "Default: " << prop.defaultValue << endl;
            }
            printProperties(prop.children, indent + 1);
        }
    }

    string serialize(const vector<Node>& nodes, int indent = 0) {
        stringstream ss;
        for (const auto& prop : nodes) {
            for (int i = 0; i < indent; ++i) ss << "  ";
            if (prop.type == "Subject") ss << "Subject " << prop.name << endl;
            else if (prop.type == "Metaclass") ss << "Metaclass " << prop.name << endl;
            else if (prop.type == "Property") ss << "Property " << prop.name << endl;
            ss << serialize(prop.children, indent + 1);
            if (prop.type != "Enum") {
                for (int i = 0; i < indent; ++i) ss << "  ";
                ss << "end" << endl;
            } else {
                for (int i = 0; i < indent; ++i) ss << "  ";
                ss << "Enum \"";
                for (size_t j = 0; j < prop.values.size(); ++j) {
                    ss << prop.values[j];
                    if (j < prop.values.size() - 1) ss << ",";
                }
                ss << "\", \"" << prop.defaultValue << "\"\n";
            }
        }
        return ss.str();
    }

public:
    void open(const string& fp) {
        filepath = fp;
        ifstream file(fp);
        stringstream buffer;
        buffer << file.rdbuf();
        parse(buffer.str());
    }

    void print_properties() {
        printProperties(structure);
    }

    void write(const string& fp) {
        ofstream file(fp);
        file << serialize(structure);
    }
};

// Example usage
// int main() {
//     PRPFile prp;
//     prp.open("example.prp");
//     prp.print_properties();
//     prp.write("output.prp");
//     return 0;
// }

To arrive at the solution: The class uses a struct for nodes and vectors for children. Parsing uses istringstream and stack. Printing and serializing are recursive functions.