All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squarespace.less.parse.DirectiveParselet Maven / Gradle / Ivy

There is a newer version: 2.0.5
Show newest version
/**
 * Copyright (c) 2014 SQUARESPACE, Inc.
 *
 * 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 com.squarespace.less.parse;

import static com.squarespace.less.parse.Parselets.BLOCK;
import static com.squarespace.less.parse.Parselets.DIRECTIVE_IMPORT;
import static com.squarespace.less.parse.Parselets.ENTITY;
import static com.squarespace.less.parse.Parselets.EXPRESSION;
import static com.squarespace.less.parse.Parselets.FEATURES;

import org.apache.commons.lang3.StringUtils;

import com.squarespace.less.LessException;
import com.squarespace.less.core.Chars;
import com.squarespace.less.model.Block;
import com.squarespace.less.model.BlockDirective;
import com.squarespace.less.model.Features;
import com.squarespace.less.model.Import;
import com.squarespace.less.model.Media;
import com.squarespace.less.model.Node;


/**
 * Main parser for all directives of the form '@' NAME.
 */
public class DirectiveParselet implements Parselet {

  @Override
  public Node parse(LessStream stm) throws LessException {
    Mark mark = stm.mark();
    stm.skipWs();
    int position = stm.position();
    if (stm.peek() != Chars.AT_SIGN || !stm.matchDirective()) {
      return null;
    }
    String name = stm.token();
    String nvName = name;
    if (name.charAt(1) == Chars.MINUS_SIGN) {
      int index = name.indexOf(Chars.MINUS_SIGN, 2);
      if (index > 0) {
        nvName = "@" + name.substring(index + 1);
      }
    }

    boolean hasBlock = false;
    boolean hasExpression = false;
    boolean hasIdentifier = false;
    switch (nvName) {

      case "@import":
      case "@import-once":
      {
        Node result = parseImport(stm, nvName, position);
        if (result == null) {
          stm.restore(mark);
        }
        return result;
      }

      case "@media":
      {
        Node result = parseMedia(stm);
        if (result == null) {
          stm.restore(mark);
        }
        return result;
      }

      case "@font-face":
      case "@viewport":
      case "@top-left":
      case "@top-left-corner":
      case "@top-center":
      case "@top-right":
      case "@top-right-corner":
      case "@bottom-left":
      case "@bottom-left-corner":
      case "@bottom-center":
      case "@bottom-right":
      case "@bottom-right-corner":
      case "@left-top":
      case "@left-middle":
      case "@left-bottom":
      case "@right-top":
      case "@right-middle":
      case "@right-bottom":
          hasBlock = true;
          break;

      case "@page":
      case "@document":
      case "@supports":
      case "@keyframes":
          hasBlock = true;
          hasIdentifier = true;
          break;

      case "@namespace":
          hasExpression = true;
          break;

      default:
        break;
    }

    if (hasIdentifier) {
      name += parseIdentifier(stm);
    }

    if (hasBlock) {
      Node block = stm.parse(BLOCK);
      if (block != null) {
        BlockDirective directive = stm.context().nodeBuilder().buildBlockDirective(name, (Block)block);
        directive.fileName(stm.fileName());
        return directive;
      }

    } else {
      Node value = parseRest(stm, hasExpression);
      if (value != null) {
        return stm.context().nodeBuilder().buildDirective(name, value);
      }
    }

    stm.restore(mark);
    return null;
  }

  private Node parseMedia(LessStream stm) throws LessException {
    Features features = (Features) stm.parse(FEATURES);
    Node block = stm.parse(BLOCK);
    if (block == null) {
      return null;
    }
    Media media = stm.context().nodeBuilder().buildMedia(features, (Block)block);
    media.fileName(stm.fileName());
    return media;
  }

  private Node parseImport(LessStream stm, String name, int position) throws LessException {
    boolean once = false;
    if (name.endsWith("-once")) {
      once = true;
    }

    // TODO: import keywords

    Node path = stm.parse(DIRECTIVE_IMPORT);
    if (path == null) {
      return null;
    }

    Features features = (Features) stm.parse(FEATURES);
    stm.skipWs();
    if (stm.seekIf(Chars.SEMICOLON)) {
      Import importNode = new Import(path, features, once);
      importNode.parseOffset(position);
      importNode.rootPath(stm.rootPath());
      importNode.fileName(stm.fileName());
      return importNode;
    }
    return null;
  }

  private String parseIdentifier(LessStream stm) throws LessException {
    StringBuilder buf = new StringBuilder();
    stm.skipWs();
    char ch = stm.peek();
    while (ch != Chars.NULL && ch != Chars.LEFT_CURLY_BRACKET && ch != Chars.LEFT_SQUARE_BRACKET) {
      buf.append(ch);
      stm.seek1();
      ch = stm.peek();
    }
    return " " + StringUtils.strip(buf.toString());
  }

  private Node parseRest(LessStream stm, boolean hasExpression) throws LessException {
    Node value = (hasExpression) ? stm.parse(EXPRESSION) : stm.parse(ENTITY);
    stm.skipWs();
    if (stm.seekIf(Chars.SEMICOLON)) {
      return value;
    }
    return null;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy