Package org.apache.sling.provisioning.model.io

Source Code of org.apache.sling.provisioning.model.io.ModelReader

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.sling.provisioning.model.io;

import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Reader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.sling.provisioning.model.Artifact;
import org.apache.sling.provisioning.model.ArtifactGroup;
import org.apache.sling.provisioning.model.Commentable;
import org.apache.sling.provisioning.model.Configuration;
import org.apache.sling.provisioning.model.Feature;
import org.apache.sling.provisioning.model.Model;
import org.apache.sling.provisioning.model.ModelConstants;
import org.apache.sling.provisioning.model.RunMode;


public class ModelReader {

    private enum CATEGORY {
        NONE(null, null),
        FEATURE("feature", new String[] {"name"}),
        VARIABLES("variables", null),
        ARTIFACTS("artifacts", new String[] {"runModes", "startLevel"}),
        SETTINGS("settings", new String[] {"runModes"}),
        CONFIGURATIONS("configurations", new String[] {"runModes"}),
        CONFIG(null, null);

        public final String name;

        public final String[] parameters;

        private CATEGORY(final String n, final String[] p) {
            this.name = n;
            this.parameters = p;
        }
    }

    /**
     * Reads the model file
     * The reader is not closed.
     * @throws IOException
     */
    public static Model read(final Reader reader, final String location)
    throws IOException {
        final ModelReader mr = new ModelReader(location);
        return mr.readModel(reader);
    }

    private CATEGORY mode = CATEGORY.NONE;

    private final Model model = new Model();

    private Feature feature = null;
    private RunMode runMode = null;
    private ArtifactGroup artifactGroup = null;
    private Configuration config = null;

    private String comment = null;

    private StringBuilder configBuilder = null;

    private LineNumberReader lineNumberReader;

    private final String exceptionPrefix;

    private ModelReader(final String location) {
        this.model.setLocation(location);
        if ( location == null ) {
            exceptionPrefix = "";
        } else {
            exceptionPrefix = location + " : ";
        }
    }

    private Model readModel(final Reader reader)
    throws IOException {

        boolean global = true;

        lineNumberReader = new LineNumberReader(reader);
        String line;
        while ( (line = lineNumberReader.readLine()) != null ) {
            // trim the line
            line = line.trim();

            // ignore empty line
            if ( line.isEmpty() ) {
                checkConfig();
                continue;
            }

            // comment?
            if ( line.startsWith("#") ) {
                if ( config != null ) {
                    configBuilder.append(line);
                    configBuilder.append('\n');

                    continue;
                }
                final String c = line.substring(1).trim();
                if ( comment == null ) {
                    comment = c;
                } else {
                    comment = comment + "\n" + c;
                }
                continue;
            }

            if ( global ) {
                global = false;
                if ( !line.startsWith("[feature ") ) {
                    throw new IOException(exceptionPrefix + " Model file must start with a feature category.");
                }
            }

            if ( line.startsWith("[") ) {
                if ( !line.endsWith("]") ) {
                    throw new IOException(exceptionPrefix + "Illegal category definition in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                }
                int pos = 1;
                while ( line.charAt(pos) != ']' && !Character.isWhitespace(line.charAt(pos))) {
                    pos++;
                }
                final String category = line.substring(1, pos);
                CATEGORY found = null;
                for (CATEGORY c : CATEGORY.values()) {
                    if ( category.equals(c.name)) {
                        found = c;
                        break;
                    }
                }
                if ( found == null ) {
                    throw new IOException(exceptionPrefix + "Unknown category in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                }
                this.mode = found;
                Map<String, String> parameters = Collections.emptyMap();
                if (line.charAt(pos) != ']') {
                    final String parameterLine = line.substring(pos + 1, line.length() - 1).trim();
                    parameters = parseParameters(parameterLine, this.mode.parameters);
                }

                switch ( this.mode ) {
                    case NONE : break; // this can never happen
                    case CONFIG : break; // this can never happen
                    case FEATURE : final String name = parameters.get("name");
                                   if ( name == null ) {
                                       throw new IOException(exceptionPrefix + "Feature name missing in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                                   }
                                   if ( model.getFeature(name) != null ) {
                                       throw new IOException(exceptionPrefix + "Duplicate feature in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                                   }
                                   this.feature = model.getOrCreateFeature(name);
                                   this.init(this.feature);
                                   this.runMode = null;
                                   this.artifactGroup = null;
                                   break;
                    case VARIABLES : checkFeature();
                                     this.init(this.feature.getVariables());
                                     break;
                    case SETTINGS: checkFeature();
                                   checkRunMode(parameters);
                                   this.init(this.runMode.getSettings());
                                   break;
                    case ARTIFACTS: checkFeature();
                                    checkRunMode(parameters);
                                    int startLevel = 0;
                                    String level = parameters.get("startLevel");
                                    if ( level != null ) {
                                        try {
                                            startLevel = Integer.valueOf(level);
                                        } catch ( final NumberFormatException nfe) {
                                            throw new IOException(exceptionPrefix + "Invalid start level in line " + this.lineNumberReader.getLineNumber() + ": " + line + ":" + level);
                                        }
                                    }
                                    if ( this.runMode.getArtifactGroup(startLevel) != null ) {
                                        throw new IOException(exceptionPrefix + "Duplicate artifact group in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                                    }
                                    this.artifactGroup = this.runMode.getOrCreateArtifactGroup(startLevel);
                                    this.init(this.artifactGroup);
                                    break;
                    case CONFIGURATIONS: checkFeature();
                                         checkRunMode(parameters);
                                         this.init(this.runMode.getConfigurations());
                                         break;
                }
            } else {
                switch ( this.mode ) {
                    case NONE : break;
                    case VARIABLES : final String[] vars = parseProperty(line);
                                     feature.getVariables().put(vars[0], vars[1]);
                                     break;
                    case SETTINGS : final String[] settings = parseProperty(line);
                                    runMode.getSettings().put(settings[0], settings[1]);
                                    break;
                    case FEATURE:   this.runMode = this.feature.getOrCreateRunMode(null);
                                    this.artifactGroup = this.runMode.getOrCreateArtifactGroup(0);
                                    // no break, we continue with ARTIFACT
                    case ARTIFACTS : String artifactUrl = line;
                                     Map<String, String> parameters = Collections.emptyMap();
                                     if ( line.endsWith("]") ) {
                                         final int startPos = line.indexOf("[");
                                         if ( startPos != -1 ) {
                                             artifactUrl = line.substring(0, startPos).trim();
                                             parameters = parseParameters(line.substring(startPos + 1, line.length() - 1).trim(), null);
                                         }
                                     }
                                     try {
                                         final Artifact artifact = Artifact.fromMvnUrl("mvn:" + artifactUrl);
                                         this.init(artifact);
                                         this.artifactGroup.add(artifact);
                                         artifact.getMetadata().putAll(parameters);
                                     } catch ( final IllegalArgumentException iae) {
                                         throw new IOException(exceptionPrefix + iae.getMessage() + " in line " + this.lineNumberReader.getLineNumber(), iae);
                                     }
                                     break;
                    case CONFIGURATIONS : String configId = line;
                                          Map<String, String> cfgPars = Collections.emptyMap();
                                          if ( line.endsWith("]") ) {
                                              final int startPos = line.indexOf("[");
                                              if ( startPos != -1 ) {
                                                  configId = line.substring(0, startPos).trim();
                                                  cfgPars = parseParameters(line.substring(startPos + 1, line.length() - 1).trim(), new String[] {"format"});
                                              }
                                          }
                                          String format = cfgPars.get("format");
                                          if ( format != null ) {
                                              if ( !ModelConstants.CFG_FORMAT_FELIX_CA.equals(format)
                                                  && !ModelConstants.CFG_FORMAT_PROPERTIES.equals(format) ) {
                                                  throw new IOException(exceptionPrefix + "Unknown format configuration parameter in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                                              }
                                          } else {
                                              format = ModelConstants.CFG_FORMAT_FELIX_CA;
                                          }
                                          final String pid;
                                          final String factoryPid;
                                          final int factoryPos = configId.indexOf('-');
                                          if ( factoryPos == -1 ) {
                                              pid = configId;
                                              factoryPid = null;
                                              config = new Configuration(configId, null);
                                          } else {
                                              pid = configId.substring(factoryPos + 1);
                                              factoryPid = configId.substring(0, factoryPos);
                                          }
                                          if ( runMode.getConfiguration(pid, factoryPid) != null ) {
                                              throw new IOException(exceptionPrefix + "Duplicate configuration in line " + this.lineNumberReader.getLineNumber());
                                          }
                                          config = runMode.getOrCreateConfiguration(pid, factoryPid);
                                          this.init(config);
                                          config.getProperties().put(ModelConstants.CFG_UNPROCESSED_FORMAT, format);
                                          configBuilder = new StringBuilder();
                                          mode = CATEGORY.CONFIG;
                                          break;
                    case CONFIG : configBuilder.append(line);
                                  configBuilder.append('\n');
                                  break;
                }
            }

        }
        checkConfig();
        if ( comment != null ) {
            throw new IOException(exceptionPrefix + "Comment not allowed at the end of file");
        }

        return model;
    }

    /**
     * Check for a feature object
     */
    private void checkFeature() throws IOException {
        if ( feature == null ) {
            throw new IOException(exceptionPrefix + "No preceding feature definition in line " + this.lineNumberReader.getLineNumber());
        }
    }

    /**
     * Check for a run mode object
     */
    private void checkRunMode(final Map<String, String> parameters) throws IOException {
        String[] runModes = null;
        final String rmDef = parameters.get("runModes");
        if ( rmDef != null ) {
            runModes = rmDef.split(",");
            for(int i=0; i<runModes.length; i++) {
                runModes[i] = runModes[i].trim();
            }
        }
        runMode = this.feature.getOrCreateRunMode(runModes);
    }

    private void init(final Commentable traceable) {
        traceable.setComment(this.comment);
        this.comment = null;
        final String number = String.valueOf(this.lineNumberReader.getLineNumber());
        if ( model.getLocation() != null ) {
            traceable.setLocation(model.getLocation() + ":" + number);
        } else {
            traceable.setLocation(number);
        }
    }

    private void checkConfig() {
        if ( config != null ) {
            config.getProperties().put(ModelConstants.CFG_UNPROCESSED, configBuilder.toString());
            this.mode = CATEGORY.CONFIGURATIONS;
        }
        config = null;
        configBuilder = null;
    }

    /**
     * Parse a single property line
     * @param line The line
     * @return The key and the value
     * @throws IOException If something goes wrong
     */
    private String[] parseProperty(final String line) throws IOException {
        final int equalsPos = line.indexOf('=');
        final String key = line.substring(0, equalsPos).trim();
        final String value = line.substring(equalsPos + 1).trim();
        if (key.isEmpty() || value.isEmpty() ) {
            throw new IOException(exceptionPrefix + "Invalid property; " + line + " in line " + this.lineNumberReader.getLineNumber());
        }
        return new String[] {key, value};
    }

    private Map<String, String> parseParameters(final String line, final String[] allowedParameters) throws IOException {
        final Map<String, String>parameters = new HashMap<String, String>();
        final String[] keyValuePairs = line.split(" ");
        for(String kv : keyValuePairs) {
            kv = kv.trim();
            if ( !kv.isEmpty() ) {
                final int sep = kv.indexOf('=');
                if ( sep == -1 ) {
                    throw new IOException(exceptionPrefix + "Invalid parameter definition in line " + this.lineNumberReader.getLineNumber() + ": " + line);
                }
                final String key = kv.substring(0, sep).trim();
                parameters.put(key, kv.substring(sep + 1).trim());

                if ( allowedParameters != null ) {
                    boolean found = false;
                    for(final String allowed : allowedParameters) {
                        if ( key.equals(allowed) ) {
                            found = true;
                            break;
                        }
                    }
                    if ( !found ) {
                        throw new IOException(exceptionPrefix + "Invalid parameter " + key + " in line " + this.lineNumberReader.getLineNumber());
                    }
                }
            }
        }
        return parameters;
    }
}

TOP

Related Classes of org.apache.sling.provisioning.model.io.ModelReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.